* port threadsafety redux
@ 2015-02-11 21:23 Andy Wingo
2015-02-13 18:35 ` David Pirotte
` (2 more replies)
0 siblings, 3 replies; 4+ messages in thread
From: Andy Wingo @ 2015-02-11 21:23 UTC (permalink / raw)
To: guile-devel
Hi!
So, threads and ports again. We didn't really come to a resolution in
this thread:
http://article.gmane.org/gmane.lisp.guile.devel/17023
To recap, in Guile 2.0 a port has mutable internal state that can be
corrupted when when multiple threads write to it at once. I ran into
this when doing some multithreaded server experiments, and fixed it in
the same way that libc fixes the issue for stdio streams:
https://www.gnu.org/software/libc/manual/html_node/Streams-and-Threads.html#Streams-and-Threads
Namely, ports can have associated recursive mutexes. They can be in a
mode in which every operation on a port grabs the mutex. The interface
to set a port into unlocked mode (à la fsetlocking) is unimplemented,
but the machinery is there.
This change fixed the crashes I was seeing, but it slows down port
operations. For an intel chip from a couple years ago the slowdown was
something on the order of 3x, for a tight putchar() loop; for Loongson
it could be as bad as 26x. Mark was unhappy with this.
Mark also made the argument that locking on port operations doesn't
always make sense. Indeed I quote from the libc documentation:
But there are situations where this is not enough and there are also
situations where this is not wanted. The implicit locking is not
enough if the program requires more than one stream function call to
happen atomically. One example would be if an output line a program
wants to generate is created by several function calls. The functions
by themselves would ensure only atomicity of their own operation, but
not atomicity over all the function calls. For this it is necessary to
perform the stream locking in the application code.
So we don't yet expose the equivalent of flockfile, but at this point
since there are still concerns out there I wanted to ask if the current
solution still makes sense.
I hope this is a fair summary of the issue.
My perspective on this is that crashes are unacceptable, and also that
it does make sense to log to stderr from multiple threads at once. When
writing to ports under error conditions you don't always have the luxury
of being able to coordinate access in some nicer way. I sympathize with
the desire to make put-char etc faster, as that means that more code can
be written in Scheme.
One possible alternate solution would be to expose ports more to Scheme
and so to make it easier and safer for Scheme to manipulate port data.
This would also make it possible to implement coroutines in Scheme that
yield when IO would block.
Or, we could just make stdio/stderr be locked by default, and some other
things not. Seems squirrely to me though.
Dunno. I would add that although there is a solution to this issue in
master, it might not make it into 2.2. There will probably be a dozen
prereleases before 2.2.0, so even if a 2.1.1 manages to make it out the
door before we come to a solution, that doesn't mean that the choices in
such a release are the right or final ones.
Regards,
Andy
--
http://wingolog.org/
^ permalink raw reply [flat|nested] 4+ messages in thread
* Re: port threadsafety redux
2015-02-11 21:23 port threadsafety redux Andy Wingo
@ 2015-02-13 18:35 ` David Pirotte
2015-02-17 12:11 ` Chris Vine
2015-03-04 10:18 ` Ludovic Courtès
2 siblings, 0 replies; 4+ messages in thread
From: David Pirotte @ 2015-02-13 18:35 UTC (permalink / raw)
To: Andy Wingo; +Cc: guile-devel
[-- Attachment #1: Type: text/plain, Size: 1265 bytes --]
Hi Andy,
> ...
> This change fixed the crashes I was seeing, but it slows down port
> operations. For an intel chip from a couple years ago the slowdown was
> something on the order of 3x, for a tight putchar() loop; for Loongson
> it could be as bad as 26x. Mark was unhappy with this.
I agree with Mark here, especially because it slows down port operation even for
single threaded code.
> But there are situations where this is not enough and there are also
> situations where this is not wanted...
Indeed.
> One possible alternate solution would be to expose ports more to Scheme
> and so to make it easier and safer for Scheme to manipulate port data.
> This would also make it possible to implement coroutines in Scheme that
> yield when IO would block.
Not only does it sounds the best approach, but I can't see drawbacks, is there any?
I am in favor of this solution: in the end, only the user knows, and the way Mark
resumed this in his email is perfect [the last 3 paragraphs]
> Or, we could just make stdio/stderr be locked by default, and some other
> things not. Seems squirrely to me though.
I would not do that, even for these ports, I'd leave them under the user
locking responsiblity.
Cheers,
David
[-- Attachment #2: OpenPGP digital signature --]
[-- Type: application/pgp-signature, Size: 473 bytes --]
^ permalink raw reply [flat|nested] 4+ messages in thread
* Re: port threadsafety redux
2015-02-11 21:23 port threadsafety redux Andy Wingo
2015-02-13 18:35 ` David Pirotte
@ 2015-02-17 12:11 ` Chris Vine
2015-03-04 10:18 ` Ludovic Courtès
2 siblings, 0 replies; 4+ messages in thread
From: Chris Vine @ 2015-02-17 12:11 UTC (permalink / raw)
To: guile-devel
On Wed, 11 Feb 2015 22:23:43 +0100
Andy Wingo <wingo@pobox.com> wrote:
> Hi!
>
> So, threads and ports again. We didn't really come to a resolution in
> this thread:
>
> http://article.gmane.org/gmane.lisp.guile.devel/17023
>
> To recap, in Guile 2.0 a port has mutable internal state that can be
> corrupted when when multiple threads write to it at once. I ran into
> this when doing some multithreaded server experiments, and fixed it in
> the same way that libc fixes the issue for stdio streams:
>
> https://www.gnu.org/software/libc/manual/html_node/Streams-and-Threads.html#Streams-and-Threads
>
> Namely, ports can have associated recursive mutexes. They can be in a
> mode in which every operation on a port grabs the mutex. The
> interface to set a port into unlocked mode (à la fsetlocking) is
> unimplemented, but the machinery is there.
>
> This change fixed the crashes I was seeing, but it slows down port
> operations. For an intel chip from a couple years ago the slowdown
> was something on the order of 3x, for a tight putchar() loop; for
> Loongson it could be as bad as 26x. Mark was unhappy with this.
>
> Mark also made the argument that locking on port operations doesn't
> always make sense. Indeed I quote from the libc documentation:
>
> But there are situations where this is not enough and there are also
> situations where this is not wanted. The implicit locking is not
> enough if the program requires more than one stream function call to
> happen atomically. One example would be if an output line a program
> wants to generate is created by several function calls. The
> functions by themselves would ensure only atomicity of their own
> operation, but not atomicity over all the function calls. For this it
> is necessary to perform the stream locking in the application code.
>
> So we don't yet expose the equivalent of flockfile, but at this point
> since there are still concerns out there I wanted to ask if the
> current solution still makes sense.
>
> I hope this is a fair summary of the issue.
>
> My perspective on this is that crashes are unacceptable, and also that
> it does make sense to log to stderr from multiple threads at once.
> When writing to ports under error conditions you don't always have
> the luxury of being able to coordinate access in some nicer way. I
> sympathize with the desire to make put-char etc faster, as that means
> that more code can be written in Scheme.
>
> One possible alternate solution would be to expose ports more to
> Scheme and so to make it easier and safer for Scheme to manipulate
> port data. This would also make it possible to implement coroutines
> in Scheme that yield when IO would block.
>
> Or, we could just make stdio/stderr be locked by default, and some
> other things not. Seems squirrely to me though.
>
> Dunno. I would add that although there is a solution to this issue in
> master, it might not make it into 2.2. There will probably be a dozen
> prereleases before 2.2.0, so even if a 2.1.1 manages to make it out
> the door before we come to a solution, that doesn't mean that the
> choices in such a release are the right or final ones.
Here is a comment from someone who is a guile user rather than a guile
developer.
Since guile provides native threads, a minimum requirement seems to me
to be that when the guile library writes to stderr on its own account,
it does so in a thread safe way (in the "doesn't crash the program"
sense). Since guile (at present) writes to stderr via a global buffered
port object, it means that that needs to be thread safe. An
alternative is for the library to write error messages directly to the
stderr file descriptor (which is intrinsically thread safe), but that
would rule out character by character error printing as with
put-char/write-char.
Beyond that, different standards which accommodate threads within
the respective standard require different things. You have referred to
POSIX.1c, which requires all its functions that operate on character
streams (represented by pointers to objects of type FILE) to be thread
safe in a data race but not interleaving sense, and provides access for
user code to the internal locks to deal with interleaving. As far as I
can tell C11 is silent on the point, even though it adopts threading
primitives and a memory model based on the C++11 one.
C++11 does not go as far as POSIX for its own i/o streams. Instead the
global objects for stdout (cout), stdin (cin) and stderr (cerr and
clog) and wide stream variants must be thread safe, but whether other
i/o objects are thread safe is up to the implementation - and generally
they are not. Synchronization is generally left where it should be,
with the user, since thread safety (in the formal data race sense) is
not of itself enough. Generally you also need to write to or read from
ports in a way which prevents any interleaving which would corrupt the
data format which is being written or read.
A compromise position is possible for guile. Ports could provide an
internal lock with user access on the POSIX flockfile()/funlockfile()
model, but whether the port itself uses the locks by default on
reading or writing (rather than by user intervention using the above
functions) could be optional on all except the default input, output and
error ports. (POSIX provides a few *_unlocked() functions for reading
or writing, but I think this should be a port configuration setting.)
Chris
^ permalink raw reply [flat|nested] 4+ messages in thread
* Re: port threadsafety redux
2015-02-11 21:23 port threadsafety redux Andy Wingo
2015-02-13 18:35 ` David Pirotte
2015-02-17 12:11 ` Chris Vine
@ 2015-03-04 10:18 ` Ludovic Courtès
2 siblings, 0 replies; 4+ messages in thread
From: Ludovic Courtès @ 2015-03-04 10:18 UTC (permalink / raw)
To: Andy Wingo; +Cc: Mark H. Weaver, guile-devel
Andy Wingo <wingo@pobox.com> skribis:
> Namely, ports can have associated recursive mutexes. They can be in a
> mode in which every operation on a port grabs the mutex. The interface
> to set a port into unlocked mode (à la fsetlocking) is unimplemented,
> but the machinery is there.
>
> This change fixed the crashes I was seeing, but it slows down port
> operations. For an intel chip from a couple years ago the slowdown was
> something on the order of 3x, for a tight putchar() loop; for Loongson
> it could be as bad as 26x. Mark was unhappy with this.
Thanks for bringing up the issue. I’d like to hear what Mark thinks.
:-)
Ludo’.
^ permalink raw reply [flat|nested] 4+ messages in thread
end of thread, other threads:[~2015-03-04 10:18 UTC | newest]
Thread overview: 4+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2015-02-11 21:23 port threadsafety redux Andy Wingo
2015-02-13 18:35 ` David Pirotte
2015-02-17 12:11 ` Chris Vine
2015-03-04 10:18 ` Ludovic Courtès
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for read-only IMAP folder(s) and NNTP newsgroup(s).