EXC_BAD_ACCESS using rolling buffer with array filter on CoreMotion data

Having spend hours trying to figure out why I'm getting a EXC_BAD_ACCESS on rare occasions, maybe someone here is able to spot the problem. In my development setup I saw it happening only once, but in our production setup, which has bugsnag integrated I see it happening a couple times a day.

Backstory; we are using CoreMotion to capture accelerometer data, the samples get pushed into a buffer. Every second we process the last 5 seconds of data and run some analysis. To keep the buffer manageable, before processing we scrub the buffer from any samples that are older than 5 seconds. This is exactly where it goes wrong... Below I'll attach thee part of relevant code, does anyone see why this is happening? As far as I see, there should not be a reason of it triggering?

The exception occurs in the cleanBuffer method, inside the filter statement.

(Sorry for the code blob, tried to reduce it as much as possible)

class Engine: NSObject {
    let bufferQueue = DispatchQueue(label: Bundle.main.bundleIdentifier! + ".engine", attributes: .concurrent)
    var accelerometerBuffer = [SensorSample]();
    var lastBufferProcess: UInt = 0;
    var lastAccelerometerSample: SensorSample?;
    var processor: Processor = Processor();
    var bufferProcessWindow: Int = 1000;
    
    // Called by seperate thread every 50 to 500ms
    func checkBuffers() -> Int? {
        let time = UInt(NSDate().timeIntervalSince1970 * 1000);
        if(lastBufferProcess == 0) {
            lastBufferProcess = time;
            return nil;
        }
        let bufferTime = time - lastBufferProcess;
        if(bufferTime >= self.bufferProcessWindow) {
            lastBufferProcess = time;
            bufferQueue.async(flags: .barrier) {
                self.accelerometerBuffer = self.cleanBuffer(
                    buffer: self.accelerometerBuffer,
                    time: time,
                    bufferSize: 5000);
            }
            return self.processBuffer();
        }
        return nil;
    }

    internal func processBuffer() -> Int? {
        var validCount = false
        var accelData = [SensorSample]()
        bufferQueue.sync {
            let accelCount = self.accelerometerBuffer.count
            if(accelCount > 3) {
                // at least 3 are needed for interpolation
                validCount = true
                accelData = self.accelerometerBuffer.map { $0.copy() as! SensorSample }
            }
        }
        if (!validCount) { return nil }
        return self.processor.process(accelBuffer: accelData);
    }
    
    internal func cleanBuffer(buffer: [SensorSample], time: UInt, bufferSize: Int) -> [SensorSample] {
        let boundry = Int(Int(time) - bufferSize);
        
        // EXC_BAD_ACCESS on the .withinBuffer command below
        return buffer.filter { $0.withinBuffer(boundry: boundry) }
    }
    
    // Called by CoreMotion thread every 10ms
    func addAccelerometerSample(x: Double, y: Double, z: Double) {
        let sample = SensorSample(x: x, y: y, z: z, time: UInt(NSDate().timeIntervalSince1970 * 1000));
        if (lastAccelerometerSample === nil || lastAccelerometerSample!.shash != sample.shash) {
            bufferQueue.async(flags: .barrier) {
                self.accelerometerBuffer.append(sample);
            }
            lastAccelerometerSample = sample;
        }
    }
}

public class SensorSample: NSObject, NSCopying {
    public var x = Double(0.0);
    public var y = Double(0.0);
    public var z = Double(0.0);
    public var shash = "";
    public var time: UInt = 0;
    
    public init(x: Double, y: Double, z: Double, time: UInt) {
        self.x = x;
        self.y = y;
        self.z = z;
        self.time = time;
        self.shash = String(x) + ":" + String(y) + ":" + String(z);
    }
    
    public func withinBuffer(boundry: Int) -> Bool {
        return self.time >= boundry;
    }
    
    public func copy(with zone: NSZone? = nil) -> Any {
        return SensorSample(x: x, y: y, z: z, time: time);
    }
}

Try turning on Address Sanitizer or Thread Sanitizer. Either of these may give you a better answer. It looks like you have a thread-safety issue, but the code you’ve reproduced doesn’t.