Proposal: SDL_CreateThread API change

(This is a proposal for an API change. No code has been written yet.)

…so, thinking about Unga’s thread debugging problem, I’m wondering if
changing the SDL_CreateThread API is a good idea for SDL 1.3:

SDL_Thread *SDL_CreateThread(
 SDL_ThreadFunction fn,
 const char *name,   // NEW!
 void *data,
 pfnSDL_CurrentBeginThread pfnBeginThread,
 pfnSDL_CurrentEndThread pfnEndThread
);

“name” is the new parameter, takes a string in UTF-8 format, which SDL
will make a copy of before returning. NULL is acceptable if you don’t
care about naming a thread.

The purpose would be to provide a meaningful identifier for each thread.

Many systems offer facilities for setting a thread name, so that, say,
their debuggers can report a human-readable identifier when one crashes.
SDL will provide this name to the system if such a facility exists, and
keep a copy too, so that the name can be queried through the SDL API as
well, with a new SDL_GetThreadName() API…

  const char *SDL_GetThreadName(SDL_Thread *thread);

…this function returns a pointer to a UTF-8 string that names the
specified thread, or NULL if it doesn’t have a name. This is internal
memory, not to be free()'d by the caller, and remains valid until the
specified thread is cleaned up by SDL_WaitThread().

There are no requirements for thread naming conventions, so long as the
string is null-terminated UTF-8, but these guidelines are helpful in
choosing a name:

If a system imposes requirements, SDL will try to munge the string for
it (truncate, etc), but the original string contents will be available
from SDL_GetThreadName().

Caveat: We could add an SDL_SetThreadName() function, but there are a
few problems with this approach. One: if your new thread crashes before
the creating thread has a chance to set the name, then you’re out of
luck. However, we can definitely set it before calling the thread’s
entry point if SDL_CreateThread is responsible for it. Two: Mac OS X
offers pthread_setname_np(), which only works on the current thread, so
we’d have to do this at the only place SDL has control of the created
thread. And three: no race conditions about changing names this way,
which is nice.

This would be a 1.3 change; no API change for 1.2.

–ryan.

First off, I think this is a good idea.

(This is a proposal for an API change. No code has been written yet.)

…so, thinking about Unga’s thread debugging problem, I’m wondering if
changing the SDL_CreateThread API is a good idea for SDL 1.3:

SDL_Thread *SDL_CreateThread(
SDL_ThreadFunction fn,
const char *name, // NEW!
void *data,
pfnSDL_CurrentBeginThread pfnBeginThread,
pfnSDL_CurrentEndThread pfnEndThread
);

Why not something like SDL_CreateNamedThread() with those same parameters,
where SDL_CreateThread() is a #define or wrapper around CreateNamedThread()
passes NULL for thread name? This would minimize 1.3 growing pains, but then
again, I don’t think you’ll spend a whole huge amount of time updating
SDL_CreateThread() instances in your codebase.

“name” is the new parameter, takes a string in UTF-8 format, which SDL will
make a copy of before returning. NULL is acceptable if you don’t care about
naming a thread.

The purpose would be to provide a meaningful identifier for each thread.

Many systems offer facilities for setting a thread name, so that, say,
their debuggers can report a human-readable identifier when one crashes. SDL
will provide this name to the system if such a facility exists, and keep a
copy too, so that the name can be queried through the SDL API as well, with
a new SDL_GetThreadName() API…

const char *SDL_GetThreadName(SDL_Thread *thread);

…this function returns a pointer to a UTF-8 string that names the
specified thread, or NULL if it doesn’t have a name. This is internal
memory, not to be free()'d by the caller, and remains valid until the
specified thread is cleaned up by SDL_WaitThread().

Is it currently required that SDL_WaitThread() be called to avoid a memory
leak? At least on Win32, the thread handle exists until you explicitly do
CloseHandle() and you can do WaitFor[Single/Multiple]Objects on it until
your heart is content. In POSIX land, pthread_join() doesn’t clean up, it
just blocks until the thread is terminated. In other words, the wait() is
separate from the destroy() behavior. Forcing SDL_WaitThread() to avoid a
memory leak seems kind of kludgey and a stumbling stone. I’m actually for
threads being a resource that is explicitly cleaned up because it forces a
sort of mental hierarchy (“so who cleans up this thread when it is done?”)
of operations.

There are no requirements for thread naming conventions, so long as the
string is null-terminated UTF-8, but these guidelines are helpful in
choosing a name:

http://stackoverflow.com/questions/149932/naming-
conventions-for-threadshttp://stackoverflow.com/questions/149932/naming-conventions-for-threads

If a system imposes requirements, SDL will try to munge the string for it
(truncate, etc), but the original string contents will be available from
SDL_GetThreadName().

Caveat: We could add an SDL_SetThreadName() function, but there are a few
problems with this approach. One: if your new thread crashes before the
creating thread has a chance to set the name, then you’re out of luck.
However, we can definitely set it before calling the thread’s entry point if
SDL_CreateThread is responsible for it. Two: Mac OS X offers
pthread_setname_np(), which only works on the current thread, so we’d have
to do this at the only place SDL has control of the created thread. And
three: no race conditions about changing names this way, which is nice.

Yeah, I’ve found that really short lived threads causes problems for race
conditions, e.g. newly created thread has initialization failure and
terminates before setThreadName() can be called by a different thread. If
you can’t specify your thread name ahead of time, just what are you doing?
The only advantage I can see is changing the thread name at run time to
include state, like “ConnectionListener [idle]” “ConnectionListener
[creating new client]”. But seriously, that’s cave man debugging anyways.

PatrickOn Tue, Sep 20, 2011 at 10:51 AM, Ryan C. Gordon wrote:

This would be a 1.3 change; no API change for 1.2.

–ryan.

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

Why not something like SDL_CreateNamedThread() with those same
parameters, where SDL_CreateThread() is a #define or wrapper around
CreateNamedThread() passes NULL for thread name? This would minimize 1.3
growing pains, but then again, I don’t think you’ll spend a whole huge
amount of time updating SDL_CreateThread() instances in your codebase.

That’s reasonable, but adding a second function is only good for
avoiding API breakage, and 1.3 is still an unstable API, as far as I’m
concerned (but we’ll see if such a change upsets a lot of people). If
it’s okay to change the existing API, it’s cleaner than having two
functions that otherwise do the same thing.

Is it currently required that SDL_WaitThread() be called to avoid a
memory leak? At least on Win32, the thread handle exists until you
explicitly do CloseHandle() and you can do
WaitFor[Single/Multiple]Objects on it until your heart is content.
In POSIX land, pthread_join() doesn’t clean up, it just blocks until the

pthread_join() does clean up, actually; this is where threads free
their resources, such as zombies in the process table.

This isn’t necessary if the thread is set to daemon mode, where it
cleans up resources as soon as it terminates…but you generally
shouldn’t do that.

SDL_WaitThread() wraps pthread_join(), and then cleans up some internal
SDL state, too…as this is where the SDL_Thread* becomes invalid, it’s
the best place to free the name string, too.

destroy() behavior. Forcing SDL_WaitThread() to avoid a memory leak
seems kind of kludgey and a stumbling stone. I’m actually for threads
being a resource that is explicitly cleaned up because it forces a sort
of mental hierarchy (“so who cleans up /this/ thread when it is done?”)
of operations.

It’s how most systems work, and how SDL always has, too. It’s like
saying “forcing free() to avoid a memory leak seems kind of kludgey.” :slight_smile:

Having a definite point where the thread joins is both useful (obtain
final return values without race conditions), and safe (what should
SDL_GetThreadID() do if you can’t know for certain when the SDL_Thread*
is no longer valid?).

On Windows, we WaitForSingleObject() and then CloseHandle() the thread
in SDL_WaitThread()…so it functions like pthread_join(). What you’re
describing, separating the wait from the close, is useful for setting up
a daemon thread on Windows, but we don’t offer such an API through SDL
at the moment.

If you can’t specify your thread name ahead of time, just
/what/ are you doing?

Agreed. The primary reason to have a separate function is to avoid
changing the SDL_CreateThread() signature, but I think it’s better to
break things and get them right in 1.3, and then lock it down and call
it 2.0.

–ryan.

Why not something like SDL_CreateNamedThread() with those same

parameters, where SDL_CreateThread() is a #define or wrapper around
CreateNamedThread() passes NULL for thread name? This would minimize 1.3
growing pains, but then again, I don’t think you’ll spend a whole huge
amount of time updating SDL_CreateThread() instances in your codebase.

That’s reasonable, but adding a second function is only good for avoiding
API breakage, and 1.3 is still an unstable API, as far as I’m concerned (but
we’ll see if such a change upsets a lot of people). If it’s okay to change
the existing API, it’s cleaner than having two functions that otherwise do
the same thing.

OK.

Is it currently required that SDL_WaitThread() be called to avoid a

memory leak? At least on Win32, the thread handle exists until you
explicitly do CloseHandle() and you can do
WaitFor[Single/Multiple]**Objects on it until your heart is content.
In POSIX land, pthread_join() doesn’t clean up, it just blocks until the

pthread_join() does clean up, actually; this is where threads free their
resources, such as zombies in the process table.

This isn’t necessary if the thread is set to daemon mode, where it cleans
up resources as soon as it terminates…but you generally shouldn’t do that.

SDL_WaitThread() wraps pthread_join(), and then cleans up some internal SDL
state, too…as this is where the SDL_Thread* becomes invalid, it’s the best
place to free the name string, too.

Hmm. I didn’t see any verbage in POSIX about cleaning up, but it does make
some degree of sense. It has to be done /some/ times, I guess. The spec does
say that multiple threads calling pthread_join() at once is invalid, but
doesn’t say that pthread_join() followed by another pthread_join() would be
invalid. I haven’t had a problem with it – I think the pthread_t object
keeps some kind of “yeah, this is dead, don’t wait” flag internally so
pthread_join() keeps returning. Doing SDL_WaitThread() 2x sounds like it
would crash though. Still, SDL isn’t POSIX, so I guess you can define
whatever semantics work and it really has less to do about the named thread
issue.

destroy() behavior. Forcing SDL_WaitThread() to avoid a memory leak

seems kind of kludgey and a stumbling stone. I’m actually for threads
being a resource that is explicitly cleaned up because it forces a sort
of mental hierarchy (“so who cleans up /this/ thread when it is done?”)
of operations.

It’s how most systems work, and how SDL always has, too. It’s like saying
"forcing free() to avoid a memory leak seems kind of kludgey." :slight_smile:

Mreh. It’s more like treating threads as condvars if you asked me. You can
wait on them to be signaled (terminated), and when you are done you destroy
them. You are saying waiting-is-freeing, which I personally see as violating
the law of least surprises, but it isn’t too strange given the underlying
APIs.

Having a definite point where the thread joins is both useful (obtain final
return values without race conditions), and safe (what should
SDL_GetThreadID() do if you can’t know for certain when the SDL_Thread* is
no longer valid?).

On Windows, we WaitForSingleObject() and then CloseHandle() the thread in
SDL_WaitThread()…so it functions like pthread_join(). What you’re
describing, separating the wait from the close, is useful for setting up a
daemon thread on Windows, but we don’t offer such an API through SDL at the
moment.

If you can’t specify your thread name ahead of time, just

/what/ are you doing?

Agreed. The primary reason to have a separate function is to avoid changing
the SDL_CreateThread() signature, but I think it’s better to break things
and get them right in 1.3, and then lock it down and call it 2.0.

OK. Well, I definitely think named threads are useful.On Tue, Sep 20, 2011 at 11:40 AM, Ryan C. Gordon wrote:

–ryan.

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

…so, thinking about Unga’s thread debugging problem, I’m wondering if
changing the SDL_CreateThread API is a good idea for SDL 1.3:

This is in revision control now; if your 1.3-based apps suddenly don’t
compile anymore because SDL_CreateThread() now takes an extra parameter,
go ahead and give your thread a name (or just pass a NULL in that extra
parameter if you don’t care).

–ryan.

Sorry to revive an old thread but the example in the wiki should be updated to reflect the change. The info in the page looks good, though.

http://wiki.libsdl.org/moin.cgi/SDL_CreateThread

Op 2011-10-05 07:03 , Ryan C. Gordon schreef:

…so, thinking about Unga’s thread debugging problem, I’m wondering if
changing the SDL_CreateThread API is a good idea for SDL 1.3:

This is in revision control now; if your 1.3-based apps suddenly don’t
compile anymore because SDL_CreateThread() now takes an extra
parameter, go ahead and give your thread a name (or just pass a NULL
in that extra parameter if you don’t care).
Maybe it’s just me (not knowing much about SDL threading), I stuffed in
the NULL for the name and then it crashed. (iOS) So I added an actual
name, and now it runs fine.

With a NULL for the name it crashes (EXC_BAD_ACCESS) at line 87 in
SDL_systhread.c–
Kees

With a NULL for the name it crashes (EXC_BAD_ACCESS) at line 87 in
SDL_systhread.c

Fixed now, thanks!

–ryan.