Primitive synchronization primitives — safe?

后端 未结 5 1978
孤城傲影
孤城傲影 2020-12-21 04:05

On constrained devices, I often find myself \"faking\" locks between 2 threads with 2 bools. Each is only read by one thread, and only written by the other. Here\'s what I m

5条回答
  •  情深已故
    2020-12-21 04:34

    This code is unsafe under almost all circumstances. On multi-core processors you will not have cache coherency between cores because bool reads and writes are not atomic operations. This means each core is not guarenteed to have the same value in the cache or even from memory if the cache from the last write hasn't been flushed.

    However, even on resource constrained single core devices this is not safe because you do not have control over the scheduler. Here is an example, for simplicty I'm going to pretend these are the only two threads on the device.

    When the ui_thread runs, the following lines of code could be run in the same timeslice.

    // new Thread(downloader_thread).start();
    // ...
    should_pause = true;
    

    The downloader_thread runs next and in it's time slice the following lines are executed:

    quitted = false;
    while(!should_quit)
    {
        fill_buffer(bfr);
    

    The scheduler prempts the downloader_thread before fill_buffer returns and then activates the ui_thread which runs.

    while(!is_paused) sleep(50);
    // resize buffer or something else non-thread-safe
    should_pause = false;
    

    The resize buffer operation is done while the downloader_thread is in the process of filling the buffer. This means the buffer is corrupted and you'll likely crash soon. It won't happen everytime, but the fact that you are filling the buffer before you set is_paused to true makes it more likely to happen, but even if you switched the order of those two operations on the downloader_thread you would still have a race condition, but you'd likely deadlock instead of corrupting the buffer.

    Incidentally, this is a type of spinlock, it just doesn't work. Spinlock's aren't very for wait times that are likely to span to many time slices cause the spin the processor. Your implmentation does sleep which is a bit nicer but the scheduler still has to run your thread and thread context switches aren't cheap. If you are waiting on a critical section or semaphore, the scheduler doesn't active your thread again till the resource has become free.

    You might be able to get away with this in some form on a specific platform/architecture, but it is really easy to make a mistake that is very hard to track down.

提交回复
热议问题