Bug: libsdl not thread-safe

If SDL_PollEvent and/or SDL_PumpEvents are used/needed instead
of using libsdl’s “event thread”, then the SDL_EventThread is NULL.

In that case SDL_Lock_EventThread and SDL_Unlock_EventThread
does nothing, and libsdl does calls to libx11 without setting
any lock, ever.

As a result, it is not possible to use libsdl by using SDL_PollEvent
and at the same time using either libsdl or libx11 from more
than one thread, except by wrapping every SDL call in a display
lock as well as libx11 calls…–
Carlo Wood <@Carlo_Wood>

If SDL_PollEvent and/or SDL_PumpEvents are used/needed instead
of using libsdl’s “event thread”, then the SDL_EventThread is NULL.

I can’t figure out what you are saying here. SDL_PollEvent and
SDL_PumpEvent are APIs. A thread is a thread. You can’t use an API
instead of a thread. You are going to have to be more clear before I
can help you.

In that case SDL_Lock_EventThread and SDL_Unlock_EventThread
does nothing, and libsdl does calls to libx11 without setting
any lock, ever.

That is correct, SDL pretty much assumes that the main thread will do
the rendering and handle input events so it does not need to lock
libx11.

As a result, it is not possible to use libsdl by using SDL_PollEvent
and at the same time using either libsdl or libx11 from more
than one thread, except by wrapping every SDL call in a display
lock as well as libx11 calls…

Mixing SDL and direct calls to libX11 is not a good idea. OTOH If you
want to use libx11 directly from a multithreaded program read the docs
on XInitThreads(). If you don’t call it libX11 will not be thread
safe.

Which version of SDL are you using?

Bob PendletonOn Sun, Jan 17, 2010 at 9:27 PM, Carlo Wood wrote:


Carlo Wood


SDL mailing list
SDL at lists.libsdl.org
http://lists.libsdl.org/listinfo.cgi/sdl-libsdl.org


±----------------------------------------------------------

SDL_PollEvent(), SDL_WaitEvent() and SDL_PumpEvents() may only be
called on the main thread, for the reasons you mentioned. You can use
SDL_PeepEvents() on any thread, since it only interacts with the event
queue and is defined to be thread safe.

I’ll make that more clear in the new wiki documentation. Thanks!On Sun, Jan 17, 2010 at 7:27 PM, Carlo Wood wrote:

If SDL_PollEvent and/or SDL_PumpEvents are used/needed instead
of using libsdl’s “event thread”, then the SDL_EventThread is NULL.

In that case SDL_Lock_EventThread and SDL_Unlock_EventThread
does nothing, and libsdl does calls to libx11 without setting
any lock, ever.

As a result, it is not possible to use libsdl by using SDL_PollEvent
and at the same time using either libsdl or libx11 from more
than one thread, except by wrapping every SDL call in a display
lock as well as libx11 calls…


Carlo Wood


SDL mailing list
SDL at lists.libsdl.org
http://lists.libsdl.org/listinfo.cgi/sdl-libsdl.org


-Sam Lantinga, Founder and President, Galaxy Gameworks LLC

That is not the point that I was trying to make.

If an application uses SDL_PollEvent() from it’s main thread,
then that implies that no locking occurs for libx11 calls (by
libsdl).

Therefore, such an application cannot do libx11 OR libsdl
calls from any other thread. I consider this a bug; libsdl
should make it possible to use SDL_PollEvent() (from the
main thread) and still have (custom) locks around every
call to libx11.

The case where I ran into this is the Second Life viewer,
which does all it’s rendering in it’s main thread and
has it’s own mainloop, calling SDL_PollEvent() from that
loop as well.

At some point, someone can open a “file chooser” (ie, to pick
a texture or sound file for upload). The file chooser is
implemented with GTK, which “requires” calling gtk_main().
If that is done from the main thread then the main loop
freezes completely, no rendering of anything else is done
anymore, but worse, the network packets from the server
aren’t handled anymore and the viewer pings out and disconnects
if one keeps the dialog open too long. In order to avoid
that one has to run this dialog in it’s own thread therefore.

However, the dialog somewhere causes calls to libx11: so,
now we have on one hand the demand to do calls to libx11
from another thread than the main loop and on the other
hand libsdl calls from the main loop that do NOT lock
the X display. Result: X message buffer corruptions and
a crash.

There are only two solutions:

  1. Wrap all calls to libsdl in a lock/unlock yourself,
  2. Consider this a bug and fix libsdl so that EVEN
    if it doesn’t run an “Event Thread”, it STILL does
    locking (preferably by calling user provided
    functions).On Mon, Jan 18, 2010 at 03:03:31PM -0800, Sam Lantinga wrote:

SDL_PollEvent(), SDL_WaitEvent() and SDL_PumpEvents() may only be
called on the main thread, for the reasons you mentioned. You can use
SDL_PeepEvents() on any thread, since it only interacts with the event
queue and is defined to be thread safe.

I’ll make that more clear in the new wiki documentation. Thanks!


Carlo Wood <@Carlo_Wood>

Please forgive me if I seem to be missing your point, but couldn’t you
solve the problem by, you know, creating a lock, and making sure you
implement your own locking policies around libx11 and SDL APIs?
Perhaps the problem is that you don’t know what SDL APIs may
ultimately make an x11 call?

Adding locks into the SDL API would add unnecessary overhead for the
most common case: developers who are not accessing the API from a
single thread, using other threads for doing other work.On Mon, Jan 18, 2010 at 7:45 PM, Carlo Wood wrote:

If an application uses SDL_PollEvent() from it’s main thread,
then that implies that no locking occurs for libx11 calls (by
libsdl).


http://codebad.com/

If an application uses SDL_PollEvent() from it’s main thread,
then that implies that no locking occurs for libx11 calls (by
libsdl).

Please forgive me if I seem to be missing your point, but couldn’t you
solve the problem by, you know, creating a lock, and making sure you
implement your own locking policies around libx11 and SDL APIs?
Perhaps the problem is that you don’t know what SDL APIs may
ultimately make an x11 call?

If that is the only way, then it would certainly help if
it were documented which sdl APIs need that lock.

Adding locks into the SDL API would add unnecessary overhead for the
most common case: developers who are not accessing the API from a
single thread, using other threads for doing other work.

But you are already calling lock/unlock functions in all
the right places, it’s just that these functions don’t do
anything. The following code would not slow down libsdl
but allow to use those calls:

Instead of the existing code:

void SDL_Lock_EventThread(void)
{
if ( SDL_EventThread && (SDL_ThreadID() != event_thread) ) {
/* Grab lock and spin until we’re sure event thread stopped */
SDL_mutexP(SDL_EventLock.lock);
while ( ! SDL_EventLock.safe ) {
SDL_Delay(1);
}
}
}

Which is exactly the same as:

void SDL_Lock_EventThread(void)
{
if ( SDL_EventThread ) {
if ( (SDL_ThreadID() != event_thread) ) {
/* Grab lock and spin until we’re sure event thread stopped */
SDL_mutexP(SDL_EventLock.lock);
while ( ! SDL_EventLock.safe ) {
SDL_Delay(1);
}
}
}
}

one could do:

static void (*SDL_CustomLock)(void);

void SDL_Lock_EventThread(void)
{
if ( SDL_EventThread ) {
if ( (SDL_ThreadID() != event_thread) ) {
/* Grab lock and spin until we’re sure event thread stopped */
SDL_mutexP(SDL_EventLock.lock);
while ( ! SDL_EventLock.safe ) {
SDL_Delay(1);
}
}
}
else if ( SDL_CustomLock )
{
SDL_CustomLock();
}
}

And add an API to set this lock function (pointer).
Same for SDL_Unlock_EventThread of course.
That would solve the issue (assuming non-SDL_EventThread
threads in libsdl do not lock SDL_EventLock directly
anywhere but always use SDL_Lock_EventThread(), which
seems to be the case).On Mon, Jan 18, 2010 at 08:34:05PM -0500, Donny Viszneki wrote:

On Mon, Jan 18, 2010 at 7:45 PM, Carlo Wood <@Carlo_Wood> wrote:


Carlo Wood <@Carlo_Wood>

I know I said this before, but take a look at XInitThreads(). If you
do not call XInitThreads() then you can not make Xlib calls from
multiple threads. It doesn’t matter if you wrap Xlib calls with your
own locks. You must call XInitThreads.

Before this discussion goes any further I think everyone needs to
examine their assumptions. The assumption that the current design of
SDL and Xlib are bugs are the ones I would examine first.

Bob PendletonOn Mon, Jan 18, 2010 at 6:45 PM, Carlo Wood wrote:

On Mon, Jan 18, 2010 at 03:03:31PM -0800, Sam Lantinga wrote:

SDL_PollEvent(), SDL_WaitEvent() and SDL_PumpEvents() may only be
called on the main thread, for the reasons you mentioned. ?You can use
SDL_PeepEvents() on any thread, since it only interacts with the event
queue and is defined to be thread safe.

I’ll make that more clear in the new wiki documentation. ?Thanks!

That is not the point that I was trying to make.

If an application uses SDL_PollEvent() from it’s main thread,
then that implies that no locking occurs for libx11 calls (by
libsdl).

Therefore, such an application cannot do libx11 OR libsdl
calls from any other thread. I consider this a bug; libsdl
should make it possible to use SDL_PollEvent() (from the
main thread) and still have (custom) locks around every
call to libx11.

The case where I ran into this is the Second Life viewer,
which does all it’s rendering in it’s main thread and
has it’s own mainloop, calling SDL_PollEvent() from that
loop as well.

At some point, someone can open a “file chooser” (ie, to pick
a texture or sound file for upload). The file chooser is
implemented with GTK, which “requires” calling gtk_main().
If that is done from the main thread then the main loop
freezes completely, no rendering of anything else is done
anymore, but worse, the network packets from the server
aren’t handled anymore and the viewer pings out and disconnects
if one keeps the dialog open too long. In order to avoid
that one has to run this dialog in it’s own thread therefore.

However, the dialog somewhere causes calls to libx11: so,
now we have on one hand the demand to do calls to libx11
from another thread than the main loop and on the other
hand libsdl calls from the main loop that do NOT lock
the X display. Result: X message buffer corruptions and
a crash.

There are only two solutions:

  1. Wrap all calls to libsdl in a lock/unlock yourself,
  2. Consider this a bug and fix libsdl so that EVEN
    ? if it doesn’t run an “Event Thread”, it STILL does
    ? locking (preferably by calling user provided
    ? functions).


Carlo Wood


SDL mailing list
SDL at lists.libsdl.org
http://lists.libsdl.org/listinfo.cgi/sdl-libsdl.org


±----------------------------------------------------------

But you are already calling lock/unlock functions in all
the right places, it’s just that these functions don’t do
anything.

You’re right. I think the reason SDL has resisted exposing the
internal locking mechanisms is because the APIs which might lock
anything probably can only be safely called from the main thread due
to constraints we can’t help on many platforms.

I vote in favor of exposing more of these locks for applications to
incorporate platform-dependent functionality via non-SDL calls, for
instance xlib.

void SDL_Lock_EventThread(void)
{
? ? ? ?if ( SDL_EventThread && (SDL_ThreadID() != event_thread) ) {
? ? ? ? ? ? ? ?/* Grab lock and spin until we’re sure event thread stopped */
? ? ? ? ? ? ? ?SDL_mutexP(SDL_EventLock.lock);
? ? ? ? ? ? ? ?while ( ! SDL_EventLock.safe ) {
? ? ? ? ? ? ? ? ? ? ? ?SDL_Delay(1);
? ? ? ? ? ? ? ?}
? ? ? ?}
}

Yuck.

Do we have the necessary concurrency primitives in SDL to begin
removing stuff like "while(!SDL_EventLock.safe) SDL_Delay(1)?"On Tue, Jan 19, 2010 at 7:57 AM, Carlo Wood wrote:


http://codebad.com/

But you are already calling lock/unlock functions in all
the right places, it’s just that these functions don’t do
anything.

You’re right. I think the reason SDL has resisted exposing the
internal locking mechanisms is because the APIs which might lock
anything probably can only be safely called from the main thread due
to constraints we can’t help on many platforms.

I vote in favor of exposing more of these locks for applications to
incorporate platform-dependent functionality via non-SDL calls, for
instance xlib.

void SDL_Lock_EventThread(void)
{
? ? ? ?if ( SDL_EventThread && (SDL_ThreadID() != event_thread) ) {
? ? ? ? ? ? ? ?/* Grab lock and spin until we’re sure event thread stopped */
? ? ? ? ? ? ? ?SDL_mutexP(SDL_EventLock.lock);
? ? ? ? ? ? ? ?while ( ! SDL_EventLock.safe ) {
? ? ? ? ? ? ? ? ? ? ? ?SDL_Delay(1);
? ? ? ? ? ? ? ?}
? ? ? ?}
}

Yuck.

Do we have the necessary concurrency primitives in SDL to begin
removing stuff like “while(!SDL_EventLock.safe) SDL_Delay(1)?”

Yeah, if you are going to use a spin lock then you might as well use
the atomic operations. :slight_smile:

But, yeah, seriously, why would you have to spin after you grab the
lock. If you have the lock, no one else has it and it is safe to enter
the critical section. Why spin after you lock the lock? I can using a
condition to tell you that the lock is locked and then spin back to
the condition if you don’t grab the lock when you come out of the
condition, I’ve used that one my self many times.

Bob PendletonOn Tue, Jan 19, 2010 at 12:33 PM, Donny Viszneki <donny.viszneki at gmail.com> wrote:

On Tue, Jan 19, 2010 at 7:57 AM, Carlo Wood wrote:


http://codebad.com/


SDL mailing list
SDL at lists.libsdl.org
http://lists.libsdl.org/listinfo.cgi/sdl-libsdl.org


±----------------------------------------------------------

Well, you’re wrong. X11 (the protocol) is not thread-safe, and libx11
is not thread-safe (reentrant) period.

Xlib is the software solution to make X11 “thread-safe”. It needs
an initialization before you can use it that way however, by calling
XInitThreads(). XInitThreads is a function of Xlib, not libx11.

I’m not using Xlib nor do I want that. Adding your own locking
around sets of commands + a call to XSync does the job fine, and
this is how libsdl does it too (look at your own code).

Anyway, it seems that Donny Viszneki does understand what I’m trying
to say. Maybe he can explain it more clearly then me.On Tue, Jan 19, 2010 at 07:48:42AM -0600, Bob Pendleton wrote:

I know I said this before, but take a look at XInitThreads(). If you
do not call XInitThreads() then you can not make Xlib calls from
multiple threads. It doesn’t matter if you wrap Xlib calls with your
own locks. You must call XInitThreads.

Before this discussion goes any further I think everyone needs to
examine their assumptions. The assumption that the current design of
SDL and Xlib are bugs are the ones I would examine first.

Bob Pendleton


Carlo Wood <@Carlo_Wood>

I know I said this before, but take a look at XInitThreads(). If you
do not call XInitThreads() then you can not make Xlib calls from
multiple threads. It doesn’t matter if you wrap Xlib calls with your
own locks. You must call XInitThreads.

Before this discussion goes any further I think everyone needs to
examine their assumptions. The assumption that the current design of
SDL and Xlib are bugs are the ones I would examine first.

Bob Pendleton

Well, you’re wrong. X11 (the protocol) is not thread-safe,

X Window System Protocol Version 11 is a protocol not code. It can not
be either thread safe or thread unsafe. There is in fact a very nice
library designed to use the X Window System Protocol Version 11 in an
asynchronous way from multiple threads.

Oh, BTW, you seem to be a bit hung up on names so I’m trying to use
the correct names everywhere not matter how much extra typing that
requires.

and libx11
is not thread-safe (reentrant) period.

That is correct. I did not say that it was. I said that if you want to
use it from multiple threads you have to tell it that you are going to
do that by calling XInitThreads. I made the sad assumption that if you
were aware of XInitThreads you would also know about XLockDisplay()
and XUnlockDisplay() and know that they are the correct way to look
access to the library. I also assumed that if you were not aware of
the standard method for using Xlib from multiple threads you would
look it up. Clearly I was wrong.

Xlib is the software solution to make X11 “thread-safe”.

No, it is not. It is the name for the lowest level library provided
for generating and receiving messages using the X Window System
Protocol Version 11. Xlib is commonly used to refer to both the source
code and the compiled library. Xlib does contain minimal support for
multiple threads.

It needs
an initialization before you can use it that way however, by calling
XInitThreads(). XInitThreads is a function of Xlib, not libx11.

Nope, XInitThreads() is in libx11. libX11 is the name of the library
file you get when you compile Xlib on a Unix like OS such as Linux.
The name of the compiled Xlib is not, to the best of my knowledge,
part of the standard.

I’m not using Xlib nor do I want that. Adding your own locking
around sets of commands + a call to XSync does the job fine, and
this is how libsdl does it too (look at your own code).

Calling Xsync causes a round trip to the server. The calling thread is
forced to wait while the round trip takes place. While that thread is
waiting no other thread can use Xlib. That means that Xsync is the
opposite of what you want to use in a multithreaded program. Using
your own mutexes is a bad idea because you can’t be sure that other
parts of the code, parts of libraries you have linked in, are not
making their own multi-threaded calls to Xlib. To make sure that Xlib
is actually being accessed properly you must use SinitThreads() and
Xlibs own lock and unlock operations.

Now it is possible that Sam has put in code that does just what you
say it does. Its been a while since I looked. But it wasn’t that long
ago that I grep-ed the whole damn source code of SDL 1.3 for Xsync and
Xflush calls and Sam and I discussed everyone of them that looked
suspicious to me. They were all used properly. But, like I said,
maybe Sam snuck something in on me.

Anyway, it seems that Donny Viszneki does understand what I’m trying
to say. Maybe he can explain it more clearly then me.

Yeah, maybe he can. Clearly I don’t know what I’m talking about. It is
funny, when I went to look up XInitThreads I grabbed my first edition
"The X Window System in a Nutshell" and it wasn’t in there, I had to
grab my more recent edition of the O’Reilly "Xlib Programming Manual"
to find it. There was no support for multithreading in Xlib when the
first edition was published. I really only keep the first editions as
souvenirs. They are pretty badly out of date. Back then I was on the X
consortium board, Tim sent first editions of all his X books to
everyone on the board. I ported X to a bunch of different computers. I
did the first 24 and 32 bit versions of the server and the first
server to support 1024x786 and larger resolutions. I designed and
implemented the first overlay extension for X. It did not make it into
the standard. I also implemented the first extension for changing the
virtual window. It required an extension because we changed the pixel
depth as well as the virtual screen. Lets see, I’m pretty sure I did
the first X server to support stereo (quad buffered 32 bit). Yeah,
that was a long time ago. Clearly I don’t know shit about X. Thanks
for pointing that out to me.

Bob PendletonOn Tue, Jan 19, 2010 at 4:39 PM, Carlo Wood wrote:

On Tue, Jan 19, 2010 at 07:48:42AM -0600, Bob Pendleton wrote:


Carlo Wood


SDL mailing list
SDL at lists.libsdl.org
http://lists.libsdl.org/listinfo.cgi/sdl-libsdl.org


±----------------------------------------------------------

I know I said this before, but take a look at XInitThreads(). If you
do not call XInitThreads() then you can not make Xlib calls from
multiple threads. It doesn’t matter if you wrap Xlib calls with your
own locks. You must call XInitThreads.

Before this discussion goes any further I think everyone needs to
examine their assumptions. The assumption that the current design of
SDL and Xlib are bugs are the ones I would examine first.

Bob Pendleton

Well, you’re wrong. X11 (the protocol) is not thread-safe,

X Window System Protocol Version 11 is a protocol not code. It can not
be either thread safe or thread unsafe.

I meant that the ordering of the messages is important, you can’t
hussle the messages of two different threads and send them to the
X server.

There is in fact a very nice
library designed to use the X Window System Protocol Version 11 in an
asynchronous way from multiple threads.
Oh, BTW, you seem to be a bit hung up on names so I’m trying to use
the correct names everywhere not matter how much extra typing that
requires.

and libx11
is not thread-safe (reentrant) period.

That is correct. I did not say that it was. I said that if you want to
use it from multiple threads you have to tell it that you are going to
do that by calling XInitThreads. I made the sad assumption that if you
were aware of XInitThreads you would also know about XLockDisplay()
and XUnlockDisplay() and know that they are the correct way to look
access to the library. I also assumed that if you were not aware of
the standard method for using Xlib from multiple threads you would
look it up. Clearly I was wrong.

I never coded for X directly in my life before, so it was not unlikely
that I made some errors :stuck_out_tongue:

Thanks for pointing this out. I looked up the webpages again that I read
from which I had gotten the idea that there were two different
libs, and there aren’t, I read it wrong. Thus, you’re right that
my current implementation (even though it works in my case)
isn’t the correct one and I’ll replace my own mutex with calls to
XLockDisplay() / XUnlockDisplay(), and add the initialization call.

[…snip…]

Anyway, it seems that Donny Viszneki does understand what I’m trying
to say. Maybe he can explain it more clearly then me.

Yeah, maybe he can. Clearly I don’t know what I’m talking about. It is

blink. I never said that. I just think we have some communication
problem because the essention of my post is still true and the story
of XInitTheads / XLockDisplay / XUnlockDisplay doesn’t change that.
Since Donny clearly understood what I meant I thought he might be
able to help here.

funny, when I went to look up XInitThreads I grabbed my first edition
"The X Window System in a Nutshell" and it wasn’t in there, I had to
grab my more recent edition of the O’Reilly "Xlib Programming Manual"
to find it. There was no support for multithreading in Xlib when the
first edition was published. I really only keep the first editions as
souvenirs. They are pretty badly out of date. Back then I was on the X
consortium board, Tim sent first editions of all his X books to
everyone on the board. I ported X to a bunch of different computers. I
did the first 24 and 32 bit versions of the server and the first
server to support 1024x786 and larger resolutions. I designed and
implemented the first overlay extension for X. It did not make it into
the standard. I also implemented the first extension for changing the
virtual window. It required an extension because we changed the pixel
depth as well as the virtual screen. Lets see, I’m pretty sure I did
the first X server to support stereo (quad buffered 32 bit). Yeah,
that was a long time ago. Clearly I don’t know shit about X. Thanks
for pointing that out to me.

Bob Pendleton

Libsdl does not allow a user to use Xlib from multiple threads,
because it doesn’t call XLockDisplay() / XUnlockDisplay().

However, it DOES call SDL_Lock_EventThread / SDL_Unlock_EventThread.
So, in order to allow users to use it in a multi-threaded application
you might consider revealing a bit more of this locking to the user
by allowing him to specify two function pointers that will be
called; or even just call XLockDisplay() / XUnlockDisplay() yourself
at all times if they are X11 users… The argument that most people
don’t need it might not be that relevant if the each call only locks
a mutex; that should be totally neglectable.

Otherwise just add this to SDL_Lock_EventThread:

if (sdl_thread_safe)
XLockDisplay();

(the complement) and allow the user to toggle the global boolean
’sdl_thread_safe’ (checking that boolean probably blends in with
the rest of the assembly instructions and will be executed in
parallel with existing code, and therefore cost 0 cpu cycles
if sdl_thread_safe is false; otherwise it will cost 1 cpu cycle…)

My original request was to add this especially for the case
where SDL_EventThread == NULL. If you want to support thread-safety
also when SDL_EventThread != NULL, you’d have to call
XLockDisplay() / XUnlockDisplay() a lot more often (namely in
the event thread, everywhere where currently SDL_EventLock is
locked). Is this really such a performance hit? Somehow I have
the feeling that sending a message over a socket completely
overshadows the locking of a mutex, not to mention that if
XInitThreads() isn’t called, XLockDisplay probably does the
same as SDL_Lock_EventThread (nothing).On Tue, Jan 19, 2010 at 07:21:17PM -0600, Bob Pendleton wrote:

On Tue, Jan 19, 2010 at 4:39 PM, Carlo Wood <@Carlo_Wood> wrote:

On Tue, Jan 19, 2010 at 07:48:42AM -0600, Bob Pendleton wrote:


Carlo Wood <@Carlo_Wood>

I know I said this before, but take a look at XInitThreads(). If you
do not call XInitThreads() then you can not make Xlib calls from
multiple threads. It doesn’t matter if you wrap Xlib calls with your
own locks. You must call XInitThreads.

Before this discussion goes any further I think everyone needs to
examine their assumptions. The assumption that the current design of
SDL and Xlib are bugs are the ones I would examine first.

Bob Pendleton

Well, you’re wrong. X11 (the protocol) is not thread-safe,

X Window System Protocol Version 11 is a protocol not code. It can not
be either thread safe or thread unsafe.

I meant that the ordering of the messages is important, you can’t
hussle the messages of two different threads and send them to the
X server.

There is in fact a very nice
library designed to use the X Window System Protocol Version 11 in an
asynchronous way from multiple threads.
Oh, BTW, you seem to be a bit hung up on names so I’m trying to use
the correct names everywhere not matter how much extra typing that
requires.

and libx11
is not thread-safe (reentrant) period.

That is correct. I did not say that it was. I said that if you want to
use it from multiple threads you have to tell it that you are going to
do that by calling XInitThreads. I made the sad assumption that if you
were aware of XInitThreads you would also know about XLockDisplay()
and XUnlockDisplay() and know that they are the correct way to look
access to the library. I also assumed that if you were not aware of
the standard method for using Xlib from multiple threads you would
look it up. Clearly I was wrong.

I never coded for X directly in my life before, so it was not unlikely
that I made some errors :stuck_out_tongue:

Thanks for pointing this out. I looked up the webpages again that I read
from which I had gotten the idea that there were two different
libs, and there aren’t, I read it wrong. Thus, you’re right that
my current implementation (even though it works in my case)
isn’t the correct one and I’ll replace my own mutex with calls to
XLockDisplay() / XUnlockDisplay(), and add the initialization call.

[…snip…]

Anyway, it seems that Donny Viszneki does understand what I’m trying
to say. Maybe he can explain it more clearly then me.

Yeah, maybe he can. Clearly I don’t know what I’m talking about. It is

blink. I never said that. I just think we have some communication
problem because the essention of my post is still true and the story
of XInitTheads / XLockDisplay / XUnlockDisplay doesn’t change that.
Since Donny clearly understood what I meant I thought he might be
able to help here.

funny, when I went to look up XInitThreads I grabbed my first edition
"The X Window System in a Nutshell" and it wasn’t in there, I had to
grab my more recent edition of the O’Reilly "Xlib Programming Manual"
to find it. There was no support for multithreading in Xlib when the
first edition was published. I really only keep the first editions as
souvenirs. They are pretty badly out of date. Back then I was on the X
consortium board, Tim sent first editions of all his X books to
everyone on the board. I ported X to a bunch of different computers. I
did the first 24 and 32 bit versions of the server and the first
server to support 1024x786 and larger resolutions. I designed and
implemented the first overlay extension for X. It did not make it into
the standard. I also implemented the first extension for changing the
virtual window. It required an extension because we changed the pixel
depth as well as the virtual screen. Lets see, I’m pretty sure I did
the first X server to support stereo (quad buffered 32 bit). Yeah,
that was a long time ago. Clearly I don’t know shit about X. Thanks
for pointing that out to me.

Sorry to take so long to get back to you. I was asked at the last
minute to teach a class that required me to have a Windows development
environment. Reinstalling windows from the restore disks should not
have taken much more that a day… But, the restore disks didn’t have
the drivers. After I got the drivers the first reinstall was undone by
my leaving an unprotected Windows machine on the Internet. Ok, so I
figured out the misconfiguration of my router/firewall… Then the
hard drive made this interesting noise. After getting a new drive the
video card started showing green random green dots. New video card…
New power supply to support the card. And, I still had to do all the
prep for the class.

I was a founding member of the First Church of Murphy…

Now, back to the problem at hand. I believe there is more here than
either of us thought there was.

Libsdl does not allow a user to use Xlib from multiple threads,
because it doesn’t call XLockDisplay() / XUnlockDisplay().

That is correct, it doesn’t. This hasn’t been a problem in the past,
but it is one now. It will become even more of a problem in the
future. The more cores, the more the desire to use them. :slight_smile:

If used properly a multithreaded X program can have multiple entities
all updating the screen at the same time. This is something that you
can not do on many other OSes. OTOH, you do pretty much have to have a
single thread reading events coming back from the X server. But, once
they are received they can be distributed to any entities that are
interested in them.

However, it DOES call SDL_Lock_EventThread / SDL_Unlock_EventThread.
So, in order to allow users to use it in a multi-threaded application
you might consider revealing a bit more of this locking to the user
by allowing him to specify two function pointers that will be
called; or even just call XLockDisplay() / XUnlockDisplay() yourself
at all times if they are X11 users… The argument that most people
don’t need it might not be that relevant if the each call only locks
a mutex; that should be totally neglectable.

This is a deeper problem than that. Wrapping the SDL event functions
in a mutex is pretty straight forward and does yield significant
performance improvements. It is well worth doing. (Take a look at
http://gameprogrammer.com/fastevents/fastevents1.html where I did just
that back in '02.) There are very good reasons why this is not a
standard part of SDL 1.2. Not so sure about 1.3.

Otherwise just add this to SDL_Lock_EventThread:

?if (sdl_thread_safe)
? ?XLockDisplay();

(the complement) and allow the user to toggle the global boolean
’sdl_thread_safe’ (checking that boolean probably blends in with
the rest of the assembly instructions and will be executed in
parallel with existing code, and therefore cost 0 cpu cycles
if sdl_thread_safe is false; otherwise it will cost 1 cpu cycle…)

My original request was to add this especially for the case
where SDL_EventThread == NULL. If you want to support thread-safety
also when SDL_EventThread != NULL, you’d have to call
XLockDisplay() / XUnlockDisplay() a lot more often (namely in
the event thread, everywhere where currently SDL_EventLock is
locked). Is this really such a performance hit? Somehow I have
the feeling that sending a message over a socket completely
overshadows the locking of a mutex, not to mention that if
XInitThreads() isn’t called, XLockDisplay probably does the
same as SDL_Lock_EventThread (nothing).

There are two reasons why SDL_EventThread will ever be NULL. The
programmer can say he does not want to use a separate event thread, or
the OS may not support reading events in a separate thread. IIRC
Windows was the main OS that doesn’t support reading events in a
separate thread. It is kind of important that SDL supports Windows.

Multithreaded access to graphics is a different problem. Each X
protocol message contains all the information needed to identify its
context. That means that so long as you set up Xlib to be thread safe
you can send commands from as many different threads as you like. That
is not true for OpenGL. Unless you are talking to a machine other than
the one running the application OpenGL calls do not create X protocol
messages and they do not identify their context. OpenGL is modal and
each thread must create, maintain, and select its own contexts.

To me it seems that we have more than two distinct problems. The
problem of making multithreaded access to Xlib work well with the SDL
event system is not one of the problems. Let me describe the problems
as I see them

SDL is a cross platform library designed to support portable
applications. SDL APIs must work the same way on many different
operating systems and graphics systems. That means that we can not
ever look at a problem on just one system. We have to make sure that
what we do can be made to work on other platforms.

How do we make the SDL event system work nicely with multiple threads
even when there is no separate event thread. I believe Sam has solved
this in 1.3, but I’m not sure. We must be sure that we can have
multiple threads sending events through the event system even if you
do not have, can not have, an event thread. No matter what, you really
can’t have more than one thread reading events. It just doesn’t work.

How do you support multithreaded access to the graphics system even on
systems where the OS or the API do not support that way of using the
graphics system? Well, really, you can’t. At least not without adding
a new layer to the API. But, in the case OpenGL and X we can use a
context (and window) per thread for OpenGL and we can make Xlib
callable from multiple threads. We don’t do that by adding X locking
calls to the SDL event thread. We do that by adding the calls to the
XLib wrappers created by SDL to call into the Xlib dynamic library. We
do have to make sure that the XGL calls used to set up OpenGL are
performed at the right times. But, that can by handled by xsync or
xflush as they already are.

You have raised an interesting issue. Being able to make Xlib calls
from multiple threads would be a nice feature to have.

Bob PendletonOn Wed, Jan 20, 2010 at 7:57 AM, Carlo Wood wrote:

On Tue, Jan 19, 2010 at 07:21:17PM -0600, Bob Pendleton wrote:

On Tue, Jan 19, 2010 at 4:39 PM, Carlo Wood wrote:

On Tue, Jan 19, 2010 at 07:48:42AM -0600, Bob Pendleton wrote:


Carlo Wood


SDL mailing list
SDL at lists.libsdl.org
http://lists.libsdl.org/listinfo.cgi/sdl-libsdl.org


±----------------------------------------------------------

How do you support multithreaded access to the graphics system even on
systems where the OS or the API do not support that way of using the
graphics system? Well, really, you can’t. At least not without adding
a new layer to the API. But, in the case OpenGL and X we can use a
context (and window) per thread for OpenGL and we can make Xlib
callable from multiple threads. We don’t do that by adding X locking
calls to the SDL event thread. We do that by adding the calls to the
XLib wrappers created by SDL to call into the Xlib dynamic library. We
do have to make sure that the XGL calls used to set up OpenGL are
performed at the right times. But, that can by handled by xsync or
xflush as they already are.

This might be from a gross over-simplification of this problem on my part, but couldn’t you (more meaning Sam) just create a queue of all rendering events in cases where rendering from multiple threads is not possible, and then perform them all on the main thread when SDL is instructed to?
Of course, this could be implemented just as easily as a layer over SDL, but then this would just add a little overhead in a case where it wouldn’t be necessary.
Maybe make this an implicit operation of SDL_Renderer for consistent behavior across platforms. I’d also suggest being able to implicitly lock the queue’s mutex for multiple-step rendering operations that should be performed in order with nothing interfering.

That might be an interesting thing for me to try sometime, even if you or Sam think it’s a bad idea. I don’t think it’d be a bad idea to try to leverage the availability of multiple processors anywhere possible, while minimizing the negative impact on systems with only one.

Quote:

How do you support multithreaded access to the graphics system even on
systems where the OS or the API do not support that way of using the
graphics system? Well, really, you can’t. At least not without adding
a new layer to the API. But, in the case OpenGL and X we can use a
context (and window) per thread for OpenGL and we can make Xlib
callable from multiple threads. We don’t do that by adding X locking
calls to the SDL event thread. We do that by adding the calls to the
XLib wrappers created by SDL to call into the Xlib dynamic library. We
do have to make sure that the XGL calls used to set up OpenGL are
performed at the right times. But, that can by handled by xsync or
xflush as they already are.

This might be from a gross over-simplification of this problem on my part,
but couldn’t you (more meaning Sam) just create a queue of all rendering
events in cases where rendering from multiple threads is not possible, and
then perform them all on the main thread when SDL is instructed to?
Of course, this could be implemented just as easily as a layer over SDL,
but then this would just add a little overhead in a case where it wouldn’t
be necessary.
Maybe make this an implicit operation of SDL_Renderer for consistent
behavior across platforms. I’d also suggest being able to implicitly lock
the queue’s mutex for multiple-step rendering operations that should be
performed in order with nothing interfering.

That might be an interesting thing for me to try sometime, even if you or
Sam think it’s a bad idea. I don’t think it’d be a bad idea to try to
leverage the availability of multiple processors anywhere possible, while
minimizing the negative impact on systems with only one.

Yes, you can do that. It is not a bad idea. That is pretty much what X
actually does. Here is the problem, an API like OpenGL has many function
calls. To do what you are proposing requires that you create a function with
the same name as every OpenGL function, that is, you have to create a one to
one mapping from your version of the API to the OpenGL API. Your functions
have to place a struct with the arguments of the function call and the ID of
the function call onto a queue. At the other end of the queue is an
interpreter that reads these structs and calls the correct OpenGL function.
That code records the results of the call and puts them on a queue along
with the ID of the call so that if a return value is needed it can be passed
back to the originating call. Aside from the amount of work involved do that
is not a technically challenging problem.

The technical challenge is in dealing with how many of the APIs expect to
immediately return a value to the calling function. Each function that
returns a value forces a round trip. That means, you have to put the
function arguments on the queue and then wait for the results of the call to
be returned on on the return queue. Getting performance in a system like
that is difficult. If it were easy there wouldn’t be a direct rendering
system that bypasses the X server. It is true that on systems with two or
more processors you can get good 3D performance using a server like
architecture. But, with two processors you be about the same performance as
you get with one processor using direct rendering. (I first ran into the
problem back in the late '80s and saw the effect of multiple processors on
the problem around '90.)

IMHO the right solution is to turn most of the X server into a device
driver. Then the problem just goes away. I saw that done in the early '90s
and it worked very well.

In my opinion the correct solution for SDL is to create an ultra high level
graphics API, or adopt something like Open Scene Graph, and use that as a
solution to the problem. OTOH, that would kind of kill the *simple" in
Simple Direct-Media Library :-).

I think this is a problem that can only really be solved by dropping support
for platforms without fully multi-processor capable OSes and APIs. You can
then add platforms as they become complaint with the needs of SDL. Does that
fit in with the SDL ideals and philosophy? I don’t think so… but Sam has
been dropping a bunch of platforms from 1.3 so we’ll see.

Bob PendletonOn Sat, Jan 30, 2010 at 5:01 PM, nfries88 wrote:


SDL mailing list
SDL at lists.libsdl.org
http://lists.libsdl.org/listinfo.cgi/sdl-libsdl.org


±----------------------------------------------------------

Maybe it is just my lack of graphics programming experience on real world
projects, but I still fail
to see what is the problem to have only one thread taking care doing the
graphics calls, while all
the others do whatever they need to.

This is the way it works in most GUI toolkits anyway, because of the added
complexity to do it otherwise.–
Paulo

On Mon, Feb 1, 2010 at 6:31 PM, Bob Pendleton wrote:

On Sat, Jan 30, 2010 at 5:01 PM, nfries88 wrote:

Quote:

How do you support multithreaded access to the graphics system even on
systems where the OS or the API do not support that way of using the
graphics system? Well, really, you can’t. At least not without adding
a new layer to the API. But, in the case OpenGL and X we can use a
context (and window) per thread for OpenGL and we can make Xlib
callable from multiple threads. We don’t do that by adding X locking
calls to the SDL event thread. We do that by adding the calls to the
XLib wrappers created by SDL to call into the Xlib dynamic library. We
do have to make sure that the XGL calls used to set up OpenGL are
performed at the right times. But, that can by handled by xsync or
xflush as they already are.

This might be from a gross over-simplification of this problem on my part,
but couldn’t you (more meaning Sam) just create a queue of all rendering
events in cases where rendering from multiple threads is not possible, and
then perform them all on the main thread when SDL is instructed to?
Of course, this could be implemented just as easily as a layer over SDL,
but then this would just add a little overhead in a case where it wouldn’t
be necessary.
Maybe make this an implicit operation of SDL_Renderer for consistent
behavior across platforms. I’d also suggest being able to implicitly lock
the queue’s mutex for multiple-step rendering operations that should be
performed in order with nothing interfering.

That might be an interesting thing for me to try sometime, even if you or
Sam think it’s a bad idea. I don’t think it’d be a bad idea to try to
leverage the availability of multiple processors anywhere possible, while
minimizing the negative impact on systems with only one.

Yes, you can do that. It is not a bad idea. That is pretty much what X
actually does. Here is the problem, an API like OpenGL has many function
calls. To do what you are proposing requires that you create a function with
the same name as every OpenGL function, that is, you have to create a one to
one mapping from your version of the API to the OpenGL API. Your functions
have to place a struct with the arguments of the function call and the ID of
the function call onto a queue. At the other end of the queue is an
interpreter that reads these structs and calls the correct OpenGL function.
That code records the results of the call and puts them on a queue along
with the ID of the call so that if a return value is needed it can be passed
back to the originating call. Aside from the amount of work involved do that
is not a technically challenging problem.

The technical challenge is in dealing with how many of the APIs expect to
immediately return a value to the calling function. Each function that
returns a value forces a round trip. That means, you have to put the
function arguments on the queue and then wait for the results of the call to
be returned on on the return queue. Getting performance in a system like
that is difficult. If it were easy there wouldn’t be a direct rendering
system that bypasses the X server. It is true that on systems with two or
more processors you can get good 3D performance using a server like
architecture. But, with two processors you be about the same performance as
you get with one processor using direct rendering. (I first ran into the
problem back in the late '80s and saw the effect of multiple processors on
the problem around '90.)

IMHO the right solution is to turn most of the X server into a device
driver. Then the problem just goes away. I saw that done in the early '90s
and it worked very well.

In my opinion the correct solution for SDL is to create an ultra high level
graphics API, or adopt something like Open Scene Graph, and use that as a
solution to the problem. OTOH, that would kind of kill the *simple" in
Simple Direct-Media Library :-).

I think this is a problem that can only really be solved by dropping
support for platforms without fully multi-processor capable OSes and APIs.
You can then add platforms as they become complaint with the needs of SDL.
Does that fit in with the SDL ideals and philosophy? I don’t think so… but
Sam has been dropping a bunch of platforms from 1.3 so we’ll see.

Bob Pendleton


SDL mailing list
SDL at lists.libsdl.org
http://lists.libsdl.org/listinfo.cgi/sdl-libsdl.org


±----------------------------------------------------------


SDL mailing list
SDL at lists.libsdl.org
http://lists.libsdl.org/listinfo.cgi/sdl-libsdl.org

Maybe it is just my lack of graphics programming experience on real world
projects, but I still fail
to see what is the problem to have only one thread taking care doing the
graphics calls, while all
the others do whatever they need to.

This is the way it works in most GUI toolkits anyway, because of the added
complexity to do it otherwise.

I agree with you. If you stick around long enough you will get sick of this
topic. It comes up fairly frequently. This time is started a little
differently and it actually lead to identifying a “problem” that I find
interesting. It usually follows a slightly different plot arc.

What follows is my opinion, I can make a very strong argument for it, and it
is based on my experience and education as a programmer and as a teacher.
But, it is still my opinion…

Consider the fact that human males are much more likely to die of accidents
than are human females. Why? Well, I’ve seen a guy climb down 1o floors on
the outside of a hotel to recover a womans shoe. He then climbed back up to
deliver the shoe to her on the balcony from which she threw it. He had made
some absurd claim about how much he wanted her. So, she threw her shoe over
the balcony and challenged him to “fetch”. What level of stupidity causes a
human male to risk his life rather than back down? The same trait leads to
some truly unique bug reports.

Think about the first time you (not the guy I’m replying to, the everybody
version of you) tried to write a game with NPCs in it. I’ll just call them
actors. The usual first approach to writing an actor leads you to write a
loop. Inside the loop you write a whole series of complex, nested, nasty
looking if statements. The Ifs test variables in the world and lead to code
that changes variables and then to code that draws the actions that result
from the decision. Then you go back to the top of the loop and start over.

If you pound on that code long enough and hard enough you can make it work.
You can make it work for one actor. When you need to handle more than one
actor you find that only one actor gets to move at a time because each actor
keeps control until it has animated its last decision. You used to see
commercial games that showed exactly that kind of behavior. You still see it
in a lot of flash games.

So what do you do when you find out that your approach can not work? Well,
you try to find a way to make it work. The first approach is to jump at
threads as a solution. It seems natural to just have a thread per actor so
they can all move at once. But, then you find out that doesn’t work either
because no matter how many threads you have you can only have as many active
threads as you have cores (or hyperthreads). So only a few of your actors
can move at once. (Threads might also cause you some memory problems if you
have a large number of actors.) But, not only don’t they move at the same
time, but the graphics doesn’t work. It doesn’t work because the graphics
system was designed so it would not work. Depending on how clever, and
stubborn, you are can keep trying to make it work. But, eventually you run
into that last wall. The one that stops you cold. You project does not work
and can not work. What do you do know?

You have painted yourself into an emotional corner. You have invested a lot
of time and effort into something that does not work. In fact, you have
invested all that time and effort into something that cannot* work. It
can’t work because of the basic design of OpenGL, or X, or DirectX, or
whatever you are using. At this point you have a couple of choices. 1) You
can accept that maybe the people who designed all that broken stuff knew
what they were doing. If you do that you have to admit that you not know as
much as they do and also accept that you have to throw all that work away
and start over. Or, 2) you can chose to believe that the designers of all
that other stuff are complete idiots. You can decide that their entire
design was based on stupidity. If you believe all that you can demand that
they, or someone else, fix their code.

Choice #2 is normal human adolescent male behavior. It is very common human
adult male behavior. Even women will fall into that behavior pattern at
times. Choice #1 is the behavior you hope to see in a seasoned professional
who has been schooled in the ways of egoless programming. It is in fact the
behavior of Saints and Bodhisattvas, not that of normal human males. (It is
a shame that no one teaches egoless programming anymore.)

Choice #1 leads to learning about state machines, event driven programming,
and an understanding of how to subdivide decisions from actions. It might
even lead to an understanding of how to use threads. It is rarely the choice
that is made the first time, or even the second time, you run into the
impossibility wall.

Choice #2 is made even more likely in the case of young male humans because
they have probably spent a lot of time bragging to their friends about their
great game and how close they are to being done with it. Young males have
been known to find new friends rather than have to admit that they had to
throw away several months worth of work. I do believe that this basic flaw
in the character of human males is the reason why so many of us die before
the age of 18. We are just to stupid to admit we don’t know everything. To
stupid to admit we said something really stupid. We would rather climb down
10 stories on the outside of a building than admit we said something stupid.
(That was not me. I would have laughed and walked away.) Too stupid to
even notice we are doing something suicidal for no reason at all.

Sad to say this is all from personal experience. Been there, done that.
After the second or third time I learned to write small test cases to verify
every technique I was going to use before I committed months of time to
doing it wrong. That habit has saved me a LOT of time. OTOH, it has
caused some absolutely absurd reactions in some managers.

Bob Pendleton

P.S.

I’ve also learned that it is damned near impossible to practice egoless
programming when you are the only ego on the project.On Mon, Feb 1, 2010 at 11:49 AM, Paulo Pinto wrote:


Paulo

On Mon, Feb 1, 2010 at 6:31 PM, Bob Pendleton <@Bob_Pendleton> wrote:

On Sat, Jan 30, 2010 at 5:01 PM, nfries88 wrote:

Quote:

How do you support multithreaded access to the graphics system even on
systems where the OS or the API do not support that way of using the
graphics system? Well, really, you can’t. At least not without adding
a new layer to the API. But, in the case OpenGL and X we can use a
context (and window) per thread for OpenGL and we can make Xlib
callable from multiple threads. We don’t do that by adding X locking
calls to the SDL event thread. We do that by adding the calls to the
XLib wrappers created by SDL to call into the Xlib dynamic library. We
do have to make sure that the XGL calls used to set up OpenGL are
performed at the right times. But, that can by handled by xsync or
xflush as they already are.

This might be from a gross over-simplification of this problem on my
part, but couldn’t you (more meaning Sam) just create a queue of all
rendering events in cases where rendering from multiple threads is not
possible, and then perform them all on the main thread when SDL is
instructed to?
Of course, this could be implemented just as easily as a layer over SDL,
but then this would just add a little overhead in a case where it wouldn’t
be necessary.
Maybe make this an implicit operation of SDL_Renderer for consistent
behavior across platforms. I’d also suggest being able to implicitly lock
the queue’s mutex for multiple-step rendering operations that should be
performed in order with nothing interfering.

That might be an interesting thing for me to try sometime, even if you or
Sam think it’s a bad idea. I don’t think it’d be a bad idea to try to
leverage the availability of multiple processors anywhere possible, while
minimizing the negative impact on systems with only one.

Yes, you can do that. It is not a bad idea. That is pretty much what X
actually does. Here is the problem, an API like OpenGL has many function
calls. To do what you are proposing requires that you create a function with
the same name as every OpenGL function, that is, you have to create a one to
one mapping from your version of the API to the OpenGL API. Your functions
have to place a struct with the arguments of the function call and the ID of
the function call onto a queue. At the other end of the queue is an
interpreter that reads these structs and calls the correct OpenGL function.
That code records the results of the call and puts them on a queue along
with the ID of the call so that if a return value is needed it can be passed
back to the originating call. Aside from the amount of work involved do that
is not a technically challenging problem.

The technical challenge is in dealing with how many of the APIs expect to
immediately return a value to the calling function. Each function that
returns a value forces a round trip. That means, you have to put the
function arguments on the queue and then wait for the results of the call to
be returned on on the return queue. Getting performance in a system like
that is difficult. If it were easy there wouldn’t be a direct rendering
system that bypasses the X server. It is true that on systems with two or
more processors you can get good 3D performance using a server like
architecture. But, with two processors you be about the same performance as
you get with one processor using direct rendering. (I first ran into the
problem back in the late '80s and saw the effect of multiple processors on
the problem around '90.)

IMHO the right solution is to turn most of the X server into a device
driver. Then the problem just goes away. I saw that done in the early '90s
and it worked very well.

In my opinion the correct solution for SDL is to create an ultra high
level graphics API, or adopt something like Open Scene Graph, and use that
as a solution to the problem. OTOH, that would kind of kill the *simple" in
Simple Direct-Media Library :-).

I think this is a problem that can only really be solved by dropping
support for platforms without fully multi-processor capable OSes and APIs.
You can then add platforms as they become complaint with the needs of SDL.
Does that fit in with the SDL ideals and philosophy? I don’t think so… but
Sam has been dropping a bunch of platforms from 1.3 so we’ll see.

Bob Pendleton


SDL mailing list
SDL at lists.libsdl.org
http://lists.libsdl.org/listinfo.cgi/sdl-libsdl.org


±----------------------------------------------------------


SDL mailing list
SDL at lists.libsdl.org
http://lists.libsdl.org/listinfo.cgi/sdl-libsdl.org


SDL mailing list
SDL at lists.libsdl.org
http://lists.libsdl.org/listinfo.cgi/sdl-libsdl.org


±----------------------------------------------------------

Very interesting reading.On Wed, Feb 3, 2010 at 12:19 AM, Bob Pendleton wrote:

On Mon, Feb 1, 2010 at 11:49 AM, Paulo Pinto <@Paulo_Pinto> wrote:

Maybe it is just my lack of graphics programming experience on real world
projects, but I still fail
to see what is the problem to have only one thread taking care doing the
graphics calls, while all
the others do whatever they need to.

This is the way it works in most GUI toolkits anyway, because of the added
complexity to do it otherwise.

I agree with you. If you stick around long enough you will get sick of this
topic. It comes up fairly frequently. This time is started a little
differently and it actually lead to identifying a “problem” that I find
interesting. It usually follows a slightly different plot arc.

What follows is my opinion, I can make a very strong argument for it, and
it is based on my experience and education as a programmer and as a teacher.
But, it is still my opinion…

Consider the fact that human males are much more likely to die of accidents
than are human females. Why? Well, I’ve seen a guy climb down 1o floors on
the outside of a hotel to recover a womans shoe. He then climbed back up to
deliver the shoe to her on the balcony from which she threw it. He had made
some absurd claim about how much he wanted her. So, she threw her shoe over
the balcony and challenged him to “fetch”. What level of stupidity causes a
human male to risk his life rather than back down? The same trait leads to
some truly unique bug reports.

Think about the first time you (not the guy I’m replying to, the everybody
version of you) tried to write a game with NPCs in it. I’ll just call them
actors. The usual first approach to writing an actor leads you to write a
loop. Inside the loop you write a whole series of complex, nested, nasty
looking if statements. The Ifs test variables in the world and lead to code
that changes variables and then to code that draws the actions that result
from the decision. Then you go back to the top of the loop and start over.

If you pound on that code long enough and hard enough you can make it work.
You can make it work for one actor. When you need to handle more than one
actor you find that only one actor gets to move at a time because each actor
keeps control until it has animated its last decision. You used to see
commercial games that showed exactly that kind of behavior. You still see it
in a lot of flash games.

So what do you do when you find out that your approach can not work? Well,
you try to find a way to make it work. The first approach is to jump at
threads as a solution. It seems natural to just have a thread per actor so
they can all move at once. But, then you find out that doesn’t work either
because no matter how many threads you have you can only have as many active
threads as you have cores (or hyperthreads). So only a few of your actors
can move at once. (Threads might also cause you some memory problems if you
have a large number of actors.) But, not only don’t they move at the same
time, but the graphics doesn’t work. It doesn’t work because the graphics
system was designed so it would not work. Depending on how clever, and
stubborn, you are can keep trying to make it work. But, eventually you run
into that last wall. The one that stops you cold. You project does not work
and can not work. What do you do know?

You have painted yourself into an emotional corner. You have invested a lot
of time and effort into something that does not work. In fact, you have
invested all that time and effort into something that cannot* work. It
can’t work because of the basic design of OpenGL, or X, or DirectX, or
whatever you are using. At this point you have a couple of choices. 1) You
can accept that maybe the people who designed all that broken stuff knew
what they were doing. If you do that you have to admit that you not know as
much as they do and also accept that you have to throw all that work away
and start over. Or, 2) you can chose to believe that the designers of all
that other stuff are complete idiots. You can decide that their entire
design was based on stupidity. If you believe all that you can demand that
they, or someone else, fix their code.

Choice #2 is normal human adolescent male behavior. It is very common human
adult male behavior. Even women will fall into that behavior pattern at
times. Choice #1 is the behavior you hope to see in a seasoned professional
who has been schooled in the ways of egoless programming. It is in fact the
behavior of Saints and Bodhisattvas, not that of normal human males. (It is
a shame that no one teaches egoless programming anymore.)

Choice #1 leads to learning about state machines, event driven programming,
and an understanding of how to subdivide decisions from actions. It might
even lead to an understanding of how to use threads. It is rarely the choice
that is made the first time, or even the second time, you run into the
impossibility wall.

Choice #2 is made even more likely in the case of young male humans because
they have probably spent a lot of time bragging to their friends about their
great game and how close they are to being done with it. Young males have
been known to find new friends rather than have to admit that they had to
throw away several months worth of work. I do believe that this basic flaw
in the character of human males is the reason why so many of us die before
the age of 18. We are just to stupid to admit we don’t know everything. To
stupid to admit we said something really stupid. We would rather climb down
10 stories on the outside of a building than admit we said something stupid.
(That was not me. I would have laughed and walked away.) Too stupid to
even notice we are doing something suicidal for no reason at all.

Sad to say this is all from personal experience. Been there, done that.
After the second or third time I learned to write small test cases to verify
every technique I was going to use before I committed months of time to
doing it wrong. That habit has saved me a LOT of time. OTOH, it has
caused some absolutely absurd reactions in some managers.

Bob Pendleton

P.S.

I’ve also learned that it is damned near impossible to practice egoless
programming when you are the only ego on the project.


Paulo

On Mon, Feb 1, 2010 at 6:31 PM, Bob Pendleton wrote:

On Sat, Jan 30, 2010 at 5:01 PM, nfries88 wrote:

Quote:

How do you support multithreaded access to the graphics system even on
systems where the OS or the API do not support that way of using the
graphics system? Well, really, you can’t. At least not without adding
a new layer to the API. But, in the case OpenGL and X we can use a
context (and window) per thread for OpenGL and we can make Xlib
callable from multiple threads. We don’t do that by adding X locking
calls to the SDL event thread. We do that by adding the calls to the
XLib wrappers created by SDL to call into the Xlib dynamic library. We
do have to make sure that the XGL calls used to set up OpenGL are
performed at the right times. But, that can by handled by xsync or
xflush as they already are.

This might be from a gross over-simplification of this problem on my
part, but couldn’t you (more meaning Sam) just create a queue of all
rendering events in cases where rendering from multiple threads is not
possible, and then perform them all on the main thread when SDL is
instructed to?
Of course, this could be implemented just as easily as a layer overSDL, but then this would just add a little overhead in a case where it
wouldn’t be necessary.
Maybe make this an implicit operation of SDL_Renderer for consistent
behavior across platforms. I’d also suggest being able to implicitly lock
the queue’s mutex for multiple-step rendering operations that should be
performed in order with nothing interfering.

That might be an interesting thing for me to try sometime, even if you
or Sam think it’s a bad idea. I don’t think it’d be a bad idea to try to
leverage the availability of multiple processors anywhere possible, while
minimizing the negative impact on systems with only one.

Yes, you can do that. It is not a bad idea. That is pretty much what X
actually does. Here is the problem, an API like OpenGL has many function
calls. To do what you are proposing requires that you create a function with
the same name as every OpenGL function, that is, you have to create a one to
one mapping from your version of the API to the OpenGL API. Your functions
have to place a struct with the arguments of the function call and the ID of
the function call onto a queue. At the other end of the queue is an
interpreter that reads these structs and calls the correct OpenGL function.
That code records the results of the call and puts them on a queue along
with the ID of the call so that if a return value is needed it can be passed
back to the originating call. Aside from the amount of work involved do that
is not a technically challenging problem.

The technical challenge is in dealing with how many of the APIs expect to
immediately return a value to the calling function. Each function that
returns a value forces a round trip. That means, you have to put the
function arguments on the queue and then wait for the results of the call to
be returned on on the return queue. Getting performance in a system like
that is difficult. If it were easy there wouldn’t be a direct rendering
system that bypasses the X server. It is true that on systems with two or
more processors you can get good 3D performance using a server like
architecture. But, with two processors you be about the same performance as
you get with one processor using direct rendering. (I first ran into the
problem back in the late '80s and saw the effect of multiple processors on
the problem around '90.)

IMHO the right solution is to turn most of the X server into a device
driver. Then the problem just goes away. I saw that done in the early '90s
and it worked very well.

In my opinion the correct solution for SDL is to create an ultra high
level graphics API, or adopt something like Open Scene Graph, and use that
as a solution to the problem. OTOH, that would kind of kill the *simple" in
Simple Direct-Media Library :-).

I think this is a problem that can only really be solved by dropping
support for platforms without fully multi-processor capable OSes and APIs.
You can then add platforms as they become complaint with the needs of SDL.
Does that fit in with the SDL ideals and philosophy? I don’t think so… but
Sam has been dropping a bunch of platforms from 1.3 so we’ll see.

Bob Pendleton


SDL mailing list
SDL at lists.libsdl.org
http://lists.libsdl.org/listinfo.cgi/sdl-libsdl.org


±----------------------------------------------------------


SDL mailing list
SDL at lists.libsdl.org
http://lists.libsdl.org/listinfo.cgi/sdl-libsdl.org


SDL mailing list
SDL at lists.libsdl.org
http://lists.libsdl.org/listinfo.cgi/sdl-libsdl.org


±----------------------------------------------------------


SDL mailing list
SDL at lists.libsdl.org
http://lists.libsdl.org/listinfo.cgi/sdl-libsdl.org

Think about the first time you (not the guy I’m replying to, the everybody version of you) tried to write a game with NPCs in it. I’ll just call them actors. The usual first approach to writing an actor leads you to write a loop. Inside the loop you write a whole series of complex, nested, nasty looking if statements. The Ifs test variables in the world and lead to code that changes variables and then to code that draws the actions that result from the decision. Then you go back to the top of the loop and start over.

If you pound on that code long enough and hard enough you can make it work. You can make it work for one actor. When you need to handle more than one actor you find that only one actor gets to move at a time because each actor keeps control until it has animated its last decision. You used to see commercial games that showed exactly that kind of behavior. You still see it in a lot of flash games.

So what do you do when you find out that your approach can not work? Well, you try to find a way to make it work. The first approach is to jump at threads as a solution. It seems natural to just have a thread per actor so they can all move at once. But, then you find out that doesn’t work either because no matter how many threads you have you can only have as many active threads as you have cores (or hyperthreads). So only a few of your actors can move at once. (Threads might also cause you some memory problems if you have a large number of actors.) But, not only don’t they move at the same time, but the graphics doesn’t work. It doesn’t work because the graphics system was designed so it would not work. Depending on how clever, and stubborn, you are can keep trying to make it work. But, eventually you run into that last wall. The one that stops you cold. You project does not work and can not work. What do you do know?

You have painted yourself into an emotional corner. You have invested a lot of time and effort into something that does not work. In fact, you have invested all that time and effort into something that cannot* work. It can’t work because of the basic design of OpenGL, or X, or DirectX, or whatever you are using. At this point you have a couple of choices. 1) You can accept that maybe the people who designed all that broken stuff knew what they were doing. If you do that you have to admit that you not know as much as they do and also accept that you have to throw all that work away and start over. Or, 2) you can chose to believe that the designers of all that other stuff are complete idiots. You can decide that their entire design was based on stupidity. If you believe all that you can demand that they, or someone else, fix their code.

Choice #2 is normal human adolescent male behavior. It is very common human adult male behavior. Even women will fall into that behavior pattern at times. Choice #1 is the behavior you hope to see in a seasoned professional who has been schooled in the ways of egoless programming. It is in fact the behavior of Saints and Bodhisattvas, not that of normal human males. (It is a shame that no one teaches egoless programming anymore.)

Choice #1 leads to learning about state machines, event driven programming, and an understanding of how to subdivide decisions from actions. It might even lead to an understanding of how to use threads. It is rarely the choice that is made the first time, or even the second time, you run into the impossibility wall.

OK, I’m a little confused by this. I needed a game engine that could animate multiple “actors” at the same time. (Of course, I knew that that was a requirement from the beginning, so maybe that helped a little.) I knew I had a framerate, and that actors would need to move at certain speeds, and usually move further than one frame’s worth of that speed would allow. So I ended up doing code that worked sort of like this: (Atrocious pseudocode, but you’ll get the idea)

for each actor in ActorList
if not actor.moving then continue
deltaVector = actor.destination - actor.position
totalDistance = PythagoreanTheorem(deltaVector)
movementVector = deltaVector / (totalDistance / actor.MovementSpeed)
actor.MoveBy(movementVector)
actor.AdvanceAnimation
if actor.position = actor.destination then actor.moving = false
end for each

This approach became obvious after a few minutes of analyzing the requirements. How in the world does someone end up on the path you described that leads to Choice #2? (And what do state machines have to do with it?)From: bob@pendleton.com (Bob Pendleton)
Subject: Re: [SDL] Bug: libsdl not thread-safe

Do your actors really just go from A to B, then stop? Most real games
want to have some sort of decision making AI, even if it is simple.
The state machine approach will support this type of implementation.

Though I do agree, I can’t see why anyone would jump to threads for
actors. It makes sense in a language like Lua where one has
lightweight, co-operative “co-routines”, but using system threads? I
think the essence of Bob’s post was correct, though one (as always)
can argue about the details.On 3 February 2010 19:58, Mason Wheeler wrote:

OK, I’m a little confused by this.? I needed a game engine that could
animate multiple “actors” at the same time.? (Of course, I knew that that
was a requirement from the beginning, so maybe that helped a little.)? I
knew I had a framerate, and that actors would need to move at certain
speeds, and usually move further than one frame’s worth of that speed would
allow.? So I ended up doing code that worked sort of like this:? (Atrocious
pseudocode, but you’ll get the idea)

for each actor in ActorList
? if not actor.moving then continue
? deltaVector = actor.destination - actor.position
? totalDistance = PythagoreanTheorem(deltaVector)
? movementVector = deltaVector / (totalDistance / actor.MovementSpeed)
? actor.MoveBy(movementVector)
? actor.AdvanceAnimation
? if actor.position = actor.destination then actor.moving = false
end for each

This approach became obvious after a few minutes of analyzing the
requirements.? How in the world does someone end up on the path you
described that leads to Choice #2?? (And what do state machines have to do
with it?)


SDL mailing list
SDL at lists.libsdl.org
http://lists.libsdl.org/listinfo.cgi/sdl-libsdl.org