When Farshad pointed out that the ReadFile and WriteFile API functions are
not thread-safe, I asked myself whether *any* API functions are
thread-safe. I also wondered what operations and data types can be
considered atomic and therefore thread-safe. Is a simple operation like
calling TEvent.ResetEvent thread-safe because it involves setting the value
of an unsigned integer or a Boolean? Is the function GetLastError
thread-safe, because it only reads an atomic data type? Is an assignment to
a double-precision variable thread-safe? Are all Get and Put operations on
pointers thread-safe because the data type is a 4-byte cardinal? Is calling
a property that internally sets an integer not thread-safe because a method
call might not be thread-safe?
TiA for any guidance,
EM
> When Farshad pointed out that the ReadFile and WriteFile API
> functions are not thread-safe, I asked myself whether *any* API
> functions are thread-safe.
It depends on the API. Some are, some are not.
> I also wondered what operations and data types can be
> considered atomic and therefore thread-safe.
32-bit integers that are properly aligned on 32-bit memory boundaries can be
accessed atomically. Anything else is not atomic. Best to use the Win32
API's Interlocked...() functions when atomic access is needed. Don't rely
on implicit atomic access.
> Is a simple operation like calling TEvent.ResetEvent thread-safe
> because it involves setting the value of an unsigned integer or a
> Boolean?
That is not what TEvent.ResetEvent() does.
Even accessing a simple integer is not thread-safe if the variable is not
aligned in memory properly. Misaligned integers can't be accessed
atomically.
> Is the function GetLastError thread-safe
Yes. The error code is stored in TLS (thread-local storage) memory on a
per-thread basis. GetLastError() and SetLastError() access the appropriate
TLS slot for the calling thread. If multiple threads call GetLastError() at
the same time, they access different memory addresses, and thus cannot step
over each other.
> because it only reads an atomic data type?
That is not what GetLastError() does.
> Is an assignment to a double-precision variable thread-safe?
No.
> Are all Get and Put operations on pointers thread-safe
> because the data type is a 4-byte cardinal?
Not if they are misaligned.
> Is calling a property that internally sets an integer not
> thread-safe because a method call might not be thread-safe?
Yes.
Gambit
> 32-bit integers that are properly aligned on 32-bit memory boundaries
> can be accessed atomically.
Could you confirm it for miltiprocessor systems too ?
What about 16-bit alignment for words ?
--
Alex
I frequently have many apps reading and writing files 'simultaneously'. I
can save a file in Word while BitTorrent is downloading err.
non-copyrighted, non-porn, data and saving it to files.
A check with the task manager shows these Windows systems/services that are
frequently involved with reading/writing files:
explorer.exe: 17 threads
scvhost.exe:74 threads
Mcshield: 37 threads
There are dozens of other processes running, most with more than one thread.
How can filesystem APIs not be thread-safe??
>I asked myself whether *any* API functions are thread-safe.
Windows is a multiprocessor, multitasking OS. Of course the vast majority
of APIs are thread-safe. If you try, you can cause problems with multiple
threads by using the APIs in an unreasonable manner but, on the whole, *the
APIs must be thread safe else Windows would only be able to run one process
with one thread at a time*. If the APIs were not thread-safe, the MTBF for
Windows would be in the order of milliseconds. You would never see your
desktop because a crash/BSOD would happen first.
There are some high-level APIs that are not thread-safe - ones concerned
with the debugger, for example. These speicalist API calls are explicitly
declared in such a manner on MSDN because they are *rare, special-purpose
exceptions to an obvious general rule that APIs on multitasking systems must
be thread-safe*.
Rgds,
Martin
>> Is a simple operation like calling TEvent.ResetEvent thread-safe
>> because it involves setting the value of an unsigned integer or a
>> Boolean?
>
> That is not what TEvent.ResetEvent() does.
I don't know what the method does in detail, but I thought that end effect
is to set the Signaled property to False.
>
> Yes. The error code is stored in TLS (thread-local storage) memory on a
> per-thread basis. GetLastError() and SetLastError() access the
> appropriate TLS slot for the calling thread. If multiple threads call
> GetLastError() at the same time, they access different memory addresses,
> and thus cannot step over each other.
>
Thanks for that insight.
>> because it only reads an atomic data type?
>
> That is not what GetLastError() does.
Whatever else GetLastError does internally, from the perspective of the
application program it appears to retrieve the value of an integer variable
owned by the OS.
>
>> Are all Get and Put operations on pointers thread-safe
>> because the data type is a 4-byte cardinal?
>
> Not if they are misaligned.
>
When could a pointer variable be misaligned? When it's the member of a
packed record or array? BTW, can objects be packed?
>> Is calling a property that internally sets an integer not
>> thread-safe because a method call might not be thread-safe?
>
> Yes.
>
Just to clarify, are you saying that the mechanics of calling a procedure or
function (i.e. allocating a context framework on the stack) is not thread
safe? This would imply that any property that uses a Getter method to return
the value of an atomic data type would not be thread-safe.
Regards,
EM
I was referring to concurrent reading and writing to the same file
(representing a serial port), not concurrent I/O operations on different
files.
> How can filesystem APIs not be thread-safe??
What I thought might not be thread-safe is to call ReadFile in one thread
and WriteFile in another on the same file.
>
> Windows is a multiprocessor, multitasking OS. Of course the vast majority
> of APIs are thread-safe. If you try, you can cause problems with multiple
> threads by using the APIs in an unreasonable manner but, on the whole,
> *the APIs must be thread safe else Windows would only be able to run one
> process with one thread at a time*. If the APIs were not thread-safe,
> the MTBF for Windows would be in the order of milliseconds. You would
> never see your desktop because a crash/BSOD would happen first.
>
That's most reassuring! So there's no need after all to wrap API I/O calls
in critical sections, mutexes, etc. Windows does that for you under the
hood. Is that right?
> There are some high-level APIs that are not thread-safe - ones concerned
> with the debugger, for example. These speicalist API calls are explicitly
> declared in such a manner on MSDN because they are *rare, special-purpose
> exceptions to an obvious general rule that APIs on multitasking systems
> must be thread-safe*.
>
Where can I find a clear statement in the MS documentation about what in the
API is, and isn't, thread safe?
Regards,
EM
> Just to clarify, are you saying that the mechanics of calling a
> procedure or function (i.e. allocating a context framework
> on the stack) is not thread safe?
That portion of a procedure/function call is always thread-safe. But once
the stack frame is set up and the main body of the code is actually running,
the code is not thread-safe if it accesses memory that is shared with other
threads without using proper synchronization.
Gambit
> Even accessing a simple integer is not thread-safe if the variable is
> not aligned in memory properly. Misaligned integers can't be
> accessed atomically.
Just out of curiosity, how can one tell whether a variable is aligned
in memory properly or not?
> Just out of curiosity, how can one tell whether a variable
> is aligned in memory properly or not?
For 32-bit CPUs, does the variable exist at a memory address that is evenly
divisible by 4?
Gambit
Reading the COM ports from one thread and writing from another is fine. I
have been doing that for decades. If it is now not OK, eg. because M$ have
broken the COM ports on multiProcessor architectures, or on Vista, then I
have not heard about it.
>> How can filesystem APIs not be thread-safe??
>
> What I thought might not be thread-safe is to call ReadFile in one thread
> and WriteFile in another on the same file.
That is an operation that is difficult to understand on a block-oriented
device like a real disk file. On a stream-oriented device like a COM port,
it's fine.
>> Windows is a multiprocessor, multitasking OS. Of course the vast
>> majority of APIs are thread-safe. If you try, you can cause problems
>> with multiple threads by using the APIs in an unreasonable manner but, on
>> the whole, *the APIs must be thread safe else Windows would only be able
>> to run one process with one thread at a time*. If the APIs were not
>> thread-safe, the MTBF for Windows would be in the order of milliseconds.
>> You would never see your desktop because a crash/BSOD would happen first.
>>
> That's most reassuring! So there's no need after all to wrap API I/O calls
> in critical sections, mutexes, etc. Windows does that for you under the
> hood. Is that right?
Yes - it has to. There is no sane alternative.
>> There are some high-level APIs that are not thread-safe - ones concerned
>> with the debugger, for example. These speicalist API calls are
>> explicitly declared in such a manner on MSDN because they are *rare,
>> special-purpose exceptions to an obvious general rule that APIs on
>> multitasking systems must be thread-safe*.
>>
> Where can I find a clear statement in the MS documentation about what in
> the API is, and isn't, thread safe?
That's easy. It's thread-safe if M$ doesn't say otherwise. It's taken as
given that the APIs on a multiThreaded OS are thread-safe. That's not to
say, as I posted before, that some operations cannot be misused for
example: VCL components and Windows forms should only be manipulated from
the thread that created them. One could seriously extrapolate this to 'the
APIs for Windows are not thread-safe', but that is stretching reality beyond
breaking point.
Rgds,
Martin
> For 32-bit CPUs, does the variable exist at a memory address that is evenly
> divisible by 4?
So for a 64-bit CPU would the fields have to be aligned on 8 byte
boundaries? This would mean that a program that does this, and is
compiled, for a 32-bit architecture would not be thread safe on a
64-bit processor.
--
Marc Rohloff [TeamB]
marc -at- marc rohloff -dot- com
> What I thought might not be thread-safe is to call ReadFile in one thread
> and WriteFile in another on the same file.
It also depends on how you define thread safe. AFAIK, Windows will
ensure that all the file descriptors, etc are not corrupted, but if
you have two threads writing to a single file then the contents of the
file are undefined.
Most reassuring summary, thanks! I shall now remove the defensive Critical
sections that I wrapped around my calls of API functions featuring a file
handle as an argument, and revert back to how the code was before.
Regards,
EM