Peter N Lewis wrote:
At 1:54 +0100 24/2/06, Frank Heckenbach wrote:
So I think we should start with a list of RTS variables and classify them to find out how to deal with each. Did you start making such a list already perhaps?
It's a good idea. No, I haven't made a start.
My solution in general is simply not to use the RTS at all (so for example, I just use my own NumToStr routine instead of using ReadStr to parse numbers, and I've always used my own memory allocation routines (except for New(obj) which can't be avoided) and file access routines.
As you prefer, but since many features, including most I/O, memory allocation and string routines, call the RTS internally, I'm not sure I'd call the remains really Pascal. ;-) And we don't actually guarantee that any particular built-in feature does/will not use RTS routines. The RTS is tied rather strictly to the compiler (unlike C and libc, e.g. -- more like GCC and the internally called libgcc.a).
This change would be incompatible with existing RTS routines,
And unfortunately also with existing Pascal standards (for `New') or dialects (for `IOResult').
Yes, that was really what I meant to say. Incompatible with not only the existing RTS (and hence programs using it), but existing Pascal standards (and hence programs using them being ported to GPC).
Or newly written for GPC, using any of those standard or dialect features, which includes pretty much everything of my own code, for example ...
although it could possible be accomplished by placing a thread safe layer underneath the thread-unsafe standard RTS API, for example (very loose pseudo code) :
procedure WriteStr( xxx ) var error: yyy; begin fh := GetReadWriteStrFDR; error := WriteStrSafe( fh, xxx ) InOutRes := error; conditionally exit with a runtime error end;
It would also likely require additions/changes to the way the compiler interacted with the RTS.
This way would not be BP compatible, since the raising of runtime errors must be done in the caller as it depends on the {$I+} setting (at the place of call).
My point was more that we could implement a thread safe RTS underneath a thread-unsafe API. People needing thread safeness could then use the thread safe API to the RTS instead of the thread unsafe standard API.
For user-called routines, sure. But IMHO the tricky parts are the compiler-called ones such as your example WriteStr, and of course, the whole I/O-result handling (which depends to a large degree on compiler magic).
Therefore (and because BP allows direct access to InOutRes), it probably has to be a global variable. -- Or at least look like one. The compiler could rewrite read and write usage of it, but what about passing it by reference? BP allows this, and I'm not sure this isn't done in practice (ATM I'm not even sure WRT my own code, though I could check that).
If we look at C, they have a similar issue with `errno'. AFAIK, thread libraries rewrite it (via a macro) to something like `(foo^)' (in Pascal syntax) where foo is a thread-dependent function or expression that returns the address of a per-thread variable. This allows all kinds of usage including reference passing (address taking in C), so it might work with some effort.
Yes, I believe errno is handled that way, as a macro to access a per-thread stored variable.
We could presumably do the same thing with InOutRes and some compiler tricks (so the compiler silently converts InOutRes := 123 to ThreadSetInOutRes( 123 ) for example). But that requires the RTS knowing about per-thread variables, so I'm not sure it is the right way to go.
This way wouldn't cope with reference passing (address taking), so we'd need something more like `ThreadGetInOutResPointer^ := 123'. But according to Waldek, this won't be necessary when the backend supports per-thread variables.
That's why I'd suggest rather than have the RTS become thread aware, instead have the RTS provide an API that is inherently thread safe because it has kicked the thread-unsafeness up to the programmer.
For some parts, this could work. And for some, we can perhaps even eliminate the problems. (Some global variables might be avoidable, though probably not many, as I usually don't use them unwarranted. Some are only there for optimization, such as LastReadWriteStrFDR, and some are mostly read-only such as ExpEpsReal, perhaps we can do something easier with them.)
For non-thread-safe routines only called by the user we can probably add thread-safe interfaces where necessary (and perhaps declare obsolete and later remove the other ones, unless they are required for compatibility with something).
But again, the real problem are the compiler-called ones. Just one example, the I/O system keeps a list of all currently open files. This list must obviously be global. So it seems its access should be somehow synchronized (mutex etc.) -- which, in this particular case is not a matter of efficiency, as opening and closing are "rare" and costly operations anyway, but it raises the questions of thread library selection and dependence. Unless perhaps, we provide hooks for the synchronization code that default to nops and that the user has to fill in if threaded. This is just a quick idea, and may be a kludge, I'd have to think about it more. (And whether it's really viable might depend on how many such areas really are there, which I don't know offhand.)
BTW, I'm aware of the irony -- hooks = global (pointer) variables, which are generally to be avoided in threading. (But obviously, these hooks would have to be set before starting threads, and not changed within threads, so they're not actually a problem AFAICS.)
Of course, apart from variables, we have to look at non-thread-safe library routines. (I'm not sure which are -- what about malloc etc. for a start? Does libc make it thread-safe already?)
That varies from system to system.
So we can only assume what's guaranteed by POSIX etc.
This is another issue which makes the whole problem quite hard. Saying the RTS (or even some API of it) is thread safe is probably not possible without qualifying what systems it is running on.
POSIX-compatible (well, mostly ;-) systems, as usual.
Anyway, I wasn't really suggesting that a major exercise be undertaken to make the RTS thread safe,
Since several people have asked for this over time, it suggest it should perhaps be done sometime. But I'm not one of them. ;-)
more to ensure people are aware that it isn't (and to keep in peoples minds that those parts of the RTS that cannot be avoided, eg parts that the compiler calls without direct knowledge by the programmer, really need to be thread safe).
Which is probably the hardest part anyway, unfortunately ...
Frank