PulseEvent is fundamentally flawed

The PulseEvent function releases one thread (or all threads, if manual-reset) which is/are waiting for the pulsed event, then returns the event to the unset state. If no threads happen to be waiting, then the event goes to the unset state without anything happening.

And there's the flaw.

How do you know whether the thread that you think is waiting on the event really is? Surely you can't use something like

WaitForSingleObject(hEvent, INFINITE);

because there is a race between the signal and the wait. The thread that the semaphore is alerting might complete all its work and pulse the event before you get around to waiting for it.

You can try using the SignalObjectAndWait function, which combines the signal and wait into a single operation. But even then, you can't be sure that the thread is waiting for the event at the moment of the pulse.

While the thread is sitting waiting for the event, a device driver or part of the kernel itself might ask to borrow the thread to do some processing (by means of a "kernel-mode APC"). During that time, the thread is not in the wait state. (It's being used by the device driver.) If the PulseEvent happens while the thread is being "borrowed", then it will not be woken from the wait, because the PulseEvent function wakes only threads that were waiting at the time the PulseEvent occurs.

Not only are you (as a user-mode program) unable to prevent kernel mode from doing this to your thread, you cannot even detect that it has occurred.

(One place where you are likely to see this sort of thing happening is if you have the debugger attached to the process, since the debugger does things like suspend and resume threads, which result in kernel APCs.)

As a result, the PulseEvent function is useless and should be avoided. It continues to exist solely for backwards compatibility.

Sidebar: This whole business with kernel APCs also means that you cannot predict which thread will be woken when you signal a semaphore, an auto-reset event, or some other synchronization object that releases a single thread when signalled. If a thread is "borrowed" to service a kernel APC, then when it is returned to the wait list, it "goes back to the end of the line". Consequently, the order of objects waiting for a kernel object is unpredictable and cannot be relied upon.

Comments (44)
  1. Dave says:

    Do you know if this behavior also occurs in the .Net Monitor.PulseAll() method ? If so, what’s the preferred way of synchronizing threads ?

  2. mschaef says:

    Was PulseEvent simply a mistake by the Windows design team, or is this problem an artifact of some subsequent change to Windows?

  3. Raymond Chen says:

    Dave: "not actually a .NET blog" – try asking somebody from the .NET team, or work through the logic yourself.

    mschaef: I don’t know the history behind PulseEvent. But given that the race condition is independent of the APC issue, it strikes me as having been flawed from the beginning.

  4. CPU says:

    Yes, it makes creating a condition variable (CV)very interesting. I wonder how posix does it.

  5. Can someone please tell me a real life situation where PulseEvent would be useful if it would not be defective?

    Somehow I feel it is a contra-intuitive use of events, which normally should indicate some state, instead of, as the name suggests, an event (which is also contra-intuitive, but we have learned it)

  6. Peter Montgomery says:


    You often answer questions with, "Ask someone from the ‘fill-in-the-blank’ team". This really helps no one. It’s not like we work at MS. None of know anyone on the ‘fill-in-the-blank’ team, or else we <would> ask them. You don’t seem to realize that for most people, you are the closest thing they have to someone who can answer inside questions about MS and windows becuase you do work there.

    If asking someone on the correct team is easy, then perhaps posting an URL to a website for that person or an email they can be reached at would be helpful. If you (who works at MS) don’t know a website or URL, then you can almost certainly guess that the great unwashed masses don’t know it either. I have a number of questions I would love to ask the developers who work on DirectSound, but it’s not like I have any way of finding out who they are and contacting them.

    This website is terrific, but every time you give this pat answer, you sound condescending. I don’t belive you intend to sound this way, but you do. Thanks again for all the interesting and informative writing, but don’t be surprised when people ask you for help that’s really not up your alley. We all see you as a little lifeline into a giant corporation that affects most of us every day.

  7. Mark Mullin says:

    what you say makes sense iff the kernel is ‘borrowing’ my threads, but I don’t understand why it’s borrowing them in the first place – does this really mean that any worker thread, say my mersenne prime hunter that never bothers anyone or asks for anything (beyond cpu cycles) may be ‘borrowed’ by the kernel or a device driver for some nefarious and utterly unrelated purpose (never mind the debugger thread issues, I think I get those)

    If so, why – isn’t a thread local to a process ? What’s the kernel want with my piddly little user processes that don’t have any resources/permissions to speak of from the kernels perspective ? Why can’t the all powerful kernel make it’s own process/threads – hell, it made mine !

    As far as the primary subject – this could be seen as a handy behavior for certain types of threads that are awakened on a regular basis but don’t require a high degree of reliability – I can think of cases where iff I have a thread that dozes off regularly and gets pulsed regularly, I might not care if it ignores a pulse and keeps dozing – and the fact that the next time I wait, I’ll wait without having to worry about it being a pointless operation because I’ve already been signalled – that actually works well here

  8. Mike Dimmick says:

    Nuts. I’m actually using PulseEvent in production code, to cause one thread to kick another in response to a keypress. It’s for a hand-held device running Windows CE which has a ‘scan’ key, but which isn’t coupled directly to the built-in barcode scanner – at least not if you don’t want your scanned characters going straight into the keyboard buffer. Instead we reprogram the keyboard driver and call the (synchronous, blocking) ‘start decode’ method exported by the scanner device driver.

    It’s easiest to handle the keypress from a window, so we use RegisterHotKey to get the key handled by a particular window. When the key’s pressed, we pulse an auto-reset event. If the scanner is currently enabled, the ‘read from scanner’ thread will be blocked on the event, it’s released, and starts decoding. There’s a small race between the application enabling the scanner and the user pressing the key, and between completing one scan and being ready to trigger the next, but I don’t think this is a problem. It’s not a great hardship if we miss an event, either. However, I might think about switching to SetEvent when the key is pressed, then ResetEvent when it’s released.

    Dave: Monitor.PulseAll is a wrapper around Monitor.ObjPulseAll, which is an internal call to the CLR internal function ObjectNative::PulseAll. This in turn wraps ObjHeader::PulseAll, which wraps SyncBlock::PulseAll. This simply sits in a loop calling SetEvent until no more threads are waiting on the object. This information can be found in the Shared Source CLI. The CLR can get away with this kind of thing precisely because it’s a managed environment and knows which threads are waiting for what.

  9. Raymond Chen says:

    If your code is running when a hardware interrupt comes in, if your code takes a page fault, if your code gets pre-empted, if your code makes a system call, use your imagination. Consult the DDK if you need more inspiration.

    "Why isn’t a thread local to a process" -> It is. But maybe the kernel needs to talk to your process. (Some other process might be doing a ReadProcessMemory out of your process, so the kernel needs to get into your process so it can read the memory.)

  10. Phaeron says:

    It seems to me that the natural replacement for flawed PulseEvent()-based code would be to use a semaphore. Unfortunately, under Win32, semaphores are kernel objects and thus involve kernel transitions. It’s not that hard to write one based on event and Interlocked* APIs, but I still wonder why fast semaphores weren’t added to the API (like critical sections vs. mutexes).

  11. Raymond Chen says:

    (Events are kernel objects too, so I’m not sure what the "unfortunately" is all about. Your old PulseEvent code was using a kernel object already.)

  12. Gene Hamilton says:

    I think what he means by "unfortunately" is that since Semaphores involve kernel transitions, performence takes a hit. (or maybe it doesn’t?) Wouldn’t it be faster to implement something like it entirely in user mode. Sure it can’t be used across process bounderies, but some cases it would be better to use something more light-weight.

  13. Norman Diamond says:

    1/5/2005 1:23 PM Raymond Chen

    > If your code is running when a hardware

    > interrupt comes in,

    then it was running not waiting on an event

    > if your code takes a page fault,

    then it was running not waiting on an event, though the first thing it might do after the page gets swapped in is begin a wait that it was attempting to do (in which case the race is due to misprogramming in the application rather than the kernel)

    > if your code gets pre-empted,

    then it was running not waiting on an event

    > if your code makes a system call,

    then it was running not waiting on an event unless the system call was to wait (in which case the race is due to misprogramming in the application rather than the kernel)

    > use your imagination.

    I don’t yet see why the kernel should borrow user threads and damage the status of whether they’re waiting or not.

    Sure just about any use of PulseEvent on an auto-reset event is likely a fundamental flaw in an application because the application is just begging to create races and damage itself. But there are cases where PulseEvent on a manual reset event would be meaningful if the kernel didn’t damage the waiting status of threads. Sure PulseEvent even with manual reset events has race conditions, but there are cases with manual reset events where that doesn’t matter because a thread that happens to go to sleep a millisecond later would still get its signal the next time it happens. When the kernel steals those signals, that causes problems.

    When does the kernel really borrow a thread that was waiting on an object, and why does the kernel do so?

  14. Jon Potter says:

    I don’t think Raymond was talking about when a thread was waiting on an object, I think he meant when you expect it to be waiting on an object. For example, your thread may have been pre-empted just before the call to WaitForSingleObject – and so it hasn’t actually entered the wait state yet, even though you may assume that it has.

  15. The KB article on the subject still claims that the problem happens *only* when a debugger is attached:

    PRB: Synchronization Failure When Debugging


    "Note that this symptom is not a bug, but rather a side effect of debugging under Windows NT. There are no current plans to change this behavior. It is also important to note that this anomaly will not occur outside of a debug environment."

    I’ll submit a change request to get this fixed.

  16. Raymond Chen says:

    Instead of "Ask someone from the XYZ team" should I just say "I don’t know"? There’s a list of Microsoft bloggers on http://www.microsoft.com/communities/blogs/ – there’s even a categorized search feature. I can’t point you at anyone in particular since **I don’t know either**.

    "If you don’t know a website or URL, then you can almost certainly guess that the great unwashed masses don’t know it either." -> But why ask me in the first place if you know the answer is going to be "How should I know?"

    I get the impression that people assume I know everything about every Microsoft technology. Which is patent nonsense. Taking an article about the kernel PulseEvent function and following up with a .NET question seems like an unfair change of topic. Yes, .NET has something that has characteristics similar to PulseEvent – but where was the expectation created that I should anything about it? "I saw a boy that looks a lot like your son, do you know where he lives?"

  17. Peter Montgomery says:

    You asked, " Instead of "Ask someone from the XYZ team" should I just say "I don’t know"? "

    In my opinion, yes. What’s wrong with, "I don’t know." Say it and you come across as honest. I tell people "I don’t know" all the time. Basically, whenever I don’t know something, that’s what I say.

    As for being surprised that people seem to think that you know everything, well don’t be. You know a ton about the inner workings of Windows, you’ve set yourself up as someone who has a lot to share because he knows a lot, and once again, you are most people’s sole link to the inner workings of Windows.

    Achieving any level of visibility or fame will automatically makes you someone who people will ask questions. I live in LA, and my vet used to appear on the morning news from time to time doing segments about pets. I saw him on TV once, and noticed that they didn’t give the name of his veterinary practice. I asked him why. He told me that it was because people would see him on TV and come to him with the worst cases he had ever seen. Folks who had been to three and four and five other vets came to him thinking that he had some sort of miracle ability becuase they had seen him on TV. The logic was that since he was on TV, he must be the best.

    This is you, Raymond. For better or worse, people will continue to ask you questions about topics you know nothing about simply because you have created a level of visiblity and fame for yourself via this blog. Probably every programmer who comes to this blog is a local IT department for friends and family. I myself get asked all the time to help out with computer problems for which I have no knowledge or experience. My background is PC, but Mac people are forever asking me to help because they know I’m a programmer and hardware designer. Do I know squat about the Mac? No, but they ask anyway because they see me as a lifeline to help them when they feel they have no other alternative. I’m not surprised by this, and neither should you.

    Trust me Raymond, I visit your blog every day. I think it’s terrific. I’m just saying that I believe that you come across as either snarky, unrealistic, or annoyed when you give the "ask someone from" answer. I don’t believe it’s your intention to do so, it just feels that way. Yes, many people are lazy and should know better, but it’s always going to be this way. If you didn’t want to teach, share, and draw attention to yourself, then you wouldn’t have started this blog. It’s a little bit like the actor who works to become a movie star and then when they are famous, is annoyed that they can’t have any time alone. I think it just comes with the territory.

    PS – I think that if you did nothing more than change your wording to, "Perhaps you can find a blog with someone from the XYZ team on the following site", that it would be more useful and still allow you to tell people you don’t know something without having to say, "I don’t know."

  18. Raymond Chen says:

    Um, Norman, we’re talking about different things. I was responding to Mike Dimmick’s scenario of a thread that is purely CPU-bound.

  19. foxyshadis says:

    He’s already indicated he doesn’t care about .Net. (When someone asks me how to pull a credit report or update a listing, I tell them to call a processor, usually Karen.) He’s gruff online, and he respects people who help themselves more than people who want him to answer all the easy questions (over and over). When did taking someone on their own terms go out of style?

  20. The canonical example for threads losing their order in the wait queues are SetThreadContext, GetThreadContext and suspending threads. The first two need to actually run code on the thread in question and probably everyone would be very suprised if/when a suspended thread which had been at the head of the wait queue for an auto-reset kernel event was the thread chosen to wake.

    This pattern is exactly how debuggers interact with their debugees, and so the easiest place to see wait reordering happen is when a process is under control of a user-mode debugger.

    These APIs aren’t only for debuggers so you can see it otherwise. I’m 99% certain that ReadProcessMemory/WriteProcessMemory does not run a kernel APC and thus won’t generate this behavior in general.

    All that said, fairness is in long-term conflict with scalability. One of the solutions to the critical section lock convoy problem is to eliminate the previous fairness guarantee that they had. (This was being discussed; I’m not sure if it’s been implemented…)

    Luckily most people know not to use kernel objects for synchronization when possible in highly scalable applications, so it might be a while before any fairness guarantees have to be more explicitly broken.

  21. Oliver says:

    This newsgroup post seems to indicate that the NT kernel has a design flaw inherited from its VMS origins, in that APCs are conceptually invisible WRT the code originally running on the thread but "damage" the thread’s context (i.e., the wait state). It suggests that a better implementation could preserve the wait state until the thread finished executing the APC then unblock if it was signalled or pulsed.

    Mike Dimmick – isn’t a SetEvent then ResetEvent equivalent to a PulseEvent because it has the same race condition (thread may not have completed WaitForSingleObject yet) and has the same problem with APCs (signal may occur while thread is executing APC and reset may occur before APC is completed). Doesn’t this mean that Signal->Reset does not fix the problems of PulseEvent?

  22. Ben Hutchings says:

    Oliver: Yes, so it’s extremely difficult to use events correctly. Condition variables are much easier to use correctly but seem to be hard to implement on Win32.

  23. Vince P says:

    Raymond: I want to be the anti-negatron to Peter’s negatron and cancel out the negative effect. I appreciate the time you take to do this blog and the grief it probably brings you time to time.

  24. Oliver says:

    "Oliver: Yes, so it’s extremely difficult to use events correctly. Condition variables are much easier to use correctly but seem to be hard to implement on Win32."

    Yes, that’s the conclusion we (the company I work for) came to. We now use a condition variable algorithm from the "Open Source POSIX Threads for Win32" group (see URL) which seems to work quite well (it’s implemented using four semaphores and a mutex/crit-sec rather than some kind of SignalObjectAndWait+event method) and we eliminate a lot more race conditions using it.

    Does anyone know if MS intends to implement a new kernel sync object in Longhorn which fixes the problems with events (a condition variable would be an excellent choice)? Even fixing PulseEvent and Signal->Reset so that they always works after a thread has entered a wait state would probably benefit quite a few apps that are currently only working by coincidence…

  25. Mike Dimmick –

    Sounds like you could replace your code with an auto-reset Event if you really wanted to.

  26. Peter Montgomery says:


    Yeah, I’m such a dick. I told Raymond his site is terrific and that I visit it everyday, but that sometimes he comes across as something I don’t think he intends to come across as. What was I thinking? I praised his site and tried to help him be a slightly better person. God, I really am an awful person. I don’t know what came over me. Next time I’ll just look the other way and hope everything works out for the best and not bother trying to offer any comments that might be taken negatively. Look, the sun is shining! The sun is shining!!

  27. A way to ask someone from team xyz is to use the newsgroup for the product. Often you’ll get a nice answer from someone of the relevant team. For example to contact the team creating DirectSound try news://microsoft.public.directx.audio.

    Also check out http://discuss.microsoft.com for mailinglists where you often get answers from Microsoft-people as well :)

  28. Norman Diamond says:

    1/5/2005 7:38 PM Jon Potter

    > I don’t think Raymond was talking about when

    > a thread was waiting on an object, I think

    > he meant when you expect it to be waiting

    > on an object.

    Ah, if he meant when a thread is expected to be "usually" waiting on an object but has a short time window when it’s not there yet. OK, if a programmer mistakes the "usually" for "definitely" ("expect_") then of course the programmer has a bug. But I thought he meant when the thread was really waiting.

    > For example, your thread may have been

    > pre-empted

    Sure, preemption is completely different from being borrowed.

    > just before the call to WaitForSingleObject

    (not adding anything here, just quoting the rest of the context).

    1/5/2005 11:53 PM Michael Grier [MSFT]

    > fairness is in long-term conflict with

    > scalability

    Fairness isn’t completely possible anyway, even without scaling. I usually target a kind of partial fairness. One process (or thread or client or whatever) might get a chance to jump the queue once without being noticed or detected, but it won’t get a chance to do so twice in a row. If you don’t succeed at all in limiting the unfairness to some degree, then some processes might starve.

  29. Shannon says:

    Wow, Peter. Truly you done me a service by not ignoring Raymond’s evil trangressions like the rest of the sheep.

  30. AC says:

    Oliver: you say "Signal->Reset doesn’t work always". Can you please explain what you exactly mean?

  31. Oliver says:

    AC – if you make a call to SetEvent, immediatly followed by a call to ResetEvent then IMHO it will exhibit the same problems of PulseEvent.

    PulseEvent has two problems – one implicit to what it does, and one caused by the design of the NT kernel.

    The first problem is a race condition caused by the fact that you cannot necessarily be sure that the thread(s) you intend to be unblocked by the call to PulseEvent have started waiting yet. The thread may still be in the middle of the kernel call to WaitForSingleObject (or even doing something else entirely), and so you cannot be sure that PulseEvent will have any effect (it only unblocks threads that are already waiting).

    The second problem is where a PulseEvent has no effect, even though one or more threads ARE waiting. This can be caused by kernel APC (Asynchronous Procedure Call – like a Unix signal or VMS AST) delivery where the kernel temporarily moves a thread out of its wait state and causes it to execute some code (e.g., I/O completion, ReadProcessMemory etc.). While the thread is executing the arbitrary code given to it by the kernel (and is no longer doing what you programmed it do!) the thread is considered to be running, not waiting on the event anymore and hence will miss the PulseEvent (e.g., when the thread finishes executing the APC, it will go back to waiting even if it was pulsed during the APC).

    You can replace a call to PulseEvent with a call to SetEvent followed by an immediate call to ResetEvent and it will exhibit exactly the same behaviour as PulseEvent including the two problems I just listed. If you set then reset the event, there will be a race condition (the thread may not have entered WaitForSingleObject before you’re call to SetEvent and if it hasn’t entered the wait state before the call to ResetEvent, it will miss the wakeup) and it could also miss a wakeup even if the thread is already waiting (if the event is signalled then reset before the thread finishes an APC, it will miss the wakeup). I personally think this should be mentioned in the documentation for PulseEvent.

    For these reasons, IMHO kernel event synchronisation objects are a bad idea for anything that requires broadcasting notifications to multiple threads (i.e., PulseEvent) – their semantics are just too broken. POSIX condition variables are a much more elegant and safer to solution to this problem and I really wish MS would supply something like them as part of the base Win32 API. At least then people would not try to emulate them in various broken ways (condition variables are hard to write under Win32, but the POSIX threads on Win32 open source project has come up with a good algorithm).

  32. Norman Diamond says:

    Re 1/7/2005 12:52 PM Oliver

    Thank you Oliver, now I understand one problem (the second of the two that you listed), though I still think the other is a problem with broken applications rather than the kernel.

    1. A thread is solidly waiting on an event.

    2. Kernel APC delivery takes the thread off of wait in order to execute kernel-provided code.

    3. Another thread does a PulseEvent on the desired event.

    4. The kernel APC completes.

    5. The kernel returns the victim thread to waiting on the event.

    The breakage is caused by a race condition between the kernel’s implementation of PulseEvent and the kernel’s implementation of APC delivery. Isn’t this easy to fix?

    In step 2 the kernel must still be keeping a record of the fact that the victim thread was waiting for the event, since otherwise step 5 would be impossible. So is it not possible for PulseEvent (in step 3) observe that record and modify it, so that step 5′ will release the thread?

  33. Raymond Chen says:

    Note: Just guessing. I don’t work on the NT kernel.

    This assumes that PulseEvent can even see that record. My guess is that it’s just implied in a local variable on the stack somewhere. Now, perhaps you could have PulseEvent grovel the stack of other threads, but consider: While the thread has been pulled off the wait by a kernel APC (push event state), that kernel APC itself issues a wait on some other event – during that wait, a second kernel APC borrows the thread (push event state 2), etc. If all of those events are considered to be "in waiting" for that thread, you now have the situation where a thread could be waiting on an unbounded number of objects (and scarier: waiting for the same event more than once?). Normally, a thread can be waiting on at most MAXIMUM_WAIT_OBJECTS objects at a time – having a fixed maximum lets you use statically-allocated memory to thread the wait list; that way you avoid encountering an out-of-memory error when trying to wait on an object.

    (It would also mean that a thread can be in a state that is simultaneously running and blocked – try explaining that to your grandmother!)

  34. Oliver says:

    Norman – as I said, the first problem I listed is not a problem with the kernel, it is a problem with PulseEvent’s semantics which make it almost impossible to use correctly (unless you use SignalObjectAndWait to ensure atomicity such that interested threads are guaranteed to be waiting before it is possible for PulseEvent to be called as Raymond suggests in his article).

    Raymond – hmmm, hadn’t considered the reentrancy issue. I think that still argues for deprecating PulseEvent (which has been done) and adding a condition variable sync object whose Broadcast operation is equivalent to PulseEvent but without the two problems we’ve been talking about.

  35. Random Reader says:

    A few APIs are also documented as using kernel APCs. See TransmitPackets() for an example.

  36. AC says:

    Oliver: I guess that "SetEvent followed by an immediate call to ResetEvent" is not covered by design. AFAIK that’s exactly why there are "auto-reset" events (second param of CreateEvent). Then the reset is done by the woken thread. Is this a solution to your problem, and if it isn’t, why?

  37. Oliver says:

    AC – I’m not actually having any problem at all – I was responding to Mike Dimmick’s post where he suggested Set->Reset as an alternative to PulseEvent.

    Auto-reset only works if you’re only trying to "pulse" a single thread. Sure, you can create multiple events, one for each thread you wish to "pulse" but why bother. The POSIX condition variable’s broadcast operation is semantically similar to PulseEvent and is not subject to these race conditions due to its atomic use of a mutex.

  38. Jonathan Gilbert says:

    I did a bit of research into how exactly the .NET synchronization works. Whether it is susceptible depends on one thing: While SetEvent followed immediately by ResetEvent might occur entirely within the window in which a thread is busy with a kernel APC, if ResetEvent is not called, will the thread unblock when the kernel APC releases it?

    .NET’s design, assuming it is relatively similar to what is released in the shared source CLI, allocates a separate kernel ‘Event’ object for each blocked thread at any given time, and it maintains its own queue of threads waiting on an object instead of relying on such a structure in the OS.

    So, what happens when the following sequence of events occurs?:

    – The event is initially unsignaled.

    – Thread #1 calls WaitForSingleObject(hEvent). It is the only thread waiting on this hEvent.

    – A kernel APC comes barging in and steals thread #1 for a bit.

    – While the APC is still in progress, thread #2 calls SetEvent(hEvent). It does NOT call ResetEvent(hEvent).

    – The APC finishes.

    Will the thread notice that the event is signalled? Or will it sit there like a zombie until the event is first Reset and then re-Set?

    If the answer is the former, then .NET’s Monitor.Pulse is immune to the effect described in this blog entry. If it is the latter, then .NET synchronization is just as easily broken as pretty much anything you write by hand using ‘Event’ objects.

  39. I might add to this issue and say that from the point of view of the user mode programmer, pulse event would be if not "flawed", then distinctly unuseful even were it not for the Kernel APC issue.

    Consider that in a WaitForXXX call, until the CPU executing that call has actually aquired the dispatcher database spinlock… and there are no guarantees on how long it will take to get to that point after having left userland. So, from the P.O.V of "Mr User", trying to work out which threads are actually waiting on the lock is an exercise in unreliability *anyway*.

    Admittedly, were it not for the APC issue, one might like to write a program where all threads have been "blocked" on the event for (say) 30 seconds, and pulse event might be a nice way of being able to say: "let’s give them all another go at performing some work item"… however, IMO, any programmer with plenty of multithreading experience worth their salt should get a prickly feeling round the back of their neck… and the thought: "but you can’t prove it to be correct". IMO far better to use semaphores or event in such a way that you can sit down with a piece (or ream) of paper, and demonstrate via diagrams or whatever, that there are no holes.


  40. Watch your access masks.

  41. It’s time for another &quot;APIs you never heard of&quot; article :)

    This time, I’d like to talk about the time*…

  42. It’s time for another &quot;APIs you never heard of&quot; article :)

    This time, I’d like to talk about the time*…

  43. For the same reason that PulseEvent is fundamentally flawed.

  44. Think of it as a semaphore with a maximum of one token.

Comments are closed.