Thread local storage API

There appears to be a potential infinite recursion bug in SDL_TLSSet:
SDL_TLSSet calls SDL_SetError, SDL_SetError calls SDL_GetErrBuf, and
SDL_GetErrBuf calls SDL_TLSSet.On 10.07.2013 11:42, Sam Lantinga wrote:

FYI, I just added a thread local storage API to the SDL RC build, in
SDL_thread.h:
SDL_TLSCreate(), SDL_TLSSet(), SDL_TLSGet()

Please let me know if you run into any problems with it!


Rainer Deyke (rainerd at eldwood.com)

[Off topic generic design discussion]

Yes, and it’s fault is exactly in being too generic to end up
promoting a feeling of being good at any job one can think of throwing
at it. Who’s going to dive into allocators and the whole other
gazillion of template parameters when all the books say “you’re fine
with default one”?

Allocators were a design error. Not aware of any other parameters.

It’s Easier to just blame the implementation for
the slowness,

STL isn’t slow. It is very fast. One of the benefits of templates.

and when it
doesn’t work, there’s one hell of a rat’s nest

That I agree with, however that’s a problem with all polymorphism.
[Although C++ does make it hard]

The way I see it is that programming languages are there to support
good design. Otherwise we’d just write hex codes (like I used to
in the old days :slight_smile:

Well, I did too :slight_smile:

Glad to meet a fellow hexer :slight_smile:

However, I prefer libraries and languages to be helpful and otherwise
stay out of the way.

It’s not so easy to design a language that provides appropriate
facilities. Certainly C is nowhere close. [if you want to look at C
with a proper type system have a look at ATS]

Thus I fully support what you argue about Uint32 and friends, but
dropping TLS … phew.

I’m not saying SDL should drop TLS, I claim the need for it is due
to bad design. Actually you presented a very good example that
doesn’t fit that model – where you want to “invade” some code without
disturbing it: profiling and debugging are good reasons to have TLS
support in the environment to keep them out of the “normal control
flow”.

I just feel indifferent.
If people think in execution paths (as opposed to data paths)
nowadays, well, they just have to learn something,
and it’s not up to library to teach them.

Actually software is a combination of both, and it’s how they’re
coupled that characterises things. IMHO. Wirth said Programs =
Algorithms + Data structures, which is control flow plus state.
And one of the key insights is that control flow is lazy evaluation.

The problem is that application programmers are the market for
libraries, and if they want crap through ignorance, the libraries end
up being crap too, and then new programmers use them and their
applications are crap, and the cycle continues with neither party
willing to sacrifice market for quality.

I spent a lot of my time trying to get better stuff into the major markets
which may have been a mistake ;(On 11/07/2013, at 6:07 AM, Alexander Sabourenkov wrote:


john skaller
@john_skaller
http://felix-lang.org

2013/7/10, john skaller <@john_skaller>:

The TI 990
only has ONE register and this is one of the best designs ever).

Having worked on different systems on assembly before, trust me, lack
of registers hurts, and I don’t mean it just from a performance
viewpoint.

Unless you have worked on the TI 990, how is your comment of any value?
Lack of registers is a really GOOD thing if you’re designing a processor for
fast context switching. The TI990 was able to run 60 terminals on a
16 bit machine.

Which still isn’t useful when you want to provide your own error
messages

Agreed. But it doesn’t prevent you doing that, it just provides
an easy way to get a message if you want one, perhaps useful
during prototyping.

There are more serious design bugs which are unavoidable yet I don’t
see you complain about that.

Perhaps because as yet I’m not aware of them?

I think the prime example is how SDL
programs still rely on the old way of running a program with a main
loop, when phone OSes (which SDL supports) are heavily event-driven
instead, to the point that SDL has to resorts to all sorts of hacks to
work nicely in such an environment. No complaints about that either?
(I guess this is going off-topic though)

No, its about SDL so it seems on topic for the list if not the Subject.
If I understand what you’re talking about (please correct me if I’m wrong)
the fact that in SDL you have to poll for events rather than just supply
callbacks is the main reason I like SDL. In fact if it had a run loop
I would not be able to use it at all.

Callback based code is utterly evil. It isn’t possible to write code
inside out like this very well. In fact this is what my language compiler
actually does: it takes “thread based” code and turns it inside out
behind the scenes to make it callback driven. The whole purpose
of the design is to save the programmer from callbacks and give
them back the “stack” so they can couple data and state in a sane
way.

Actually Felix uses heap allocated stack stack frames to do this
in principle (and optimises it away when possible).

I call this active programming. For example in a game you can
program each NPC independently as an active agent, not a subroutine
that keeps losing its program counter. I will demonstrate some game code later
using this to show how vastly superior it is. Suffice it to say without
control inversion ordinary programming would be well nigh impossible.
If you think you can write programs which, instead of reading and writing
files, are called with data from a file, or requests for a file, you have another
think coming. Just try it. You’ll get completely lost very quickly.

Control inversion is the primary function of the operating system.

Developers are going to want to use TLS at some point, may as well do
the hard work and handle it for them than having everybody reinvent
the wheel in the worst way possible. At least this is something you
can avoid if you think TLS is a bad thing.

Only if I don’t want to use some add on someone wrote that uses it.
Whereas if it isn’t there they can’t :)On 11/07/2013, at 6:29 AM, Sik the hedgehog wrote:


john skaller
@john_skaller
http://felix-lang.org

Message-ID:

Content-Type: text/plain; charset=us-ascii

Windows has these too. They’re like globals but thread-specific.

I know what they are :slight_smile:

As you say, they’re like globals: a design fault.

Used correctly, globals have no reason to be a design fault: after
all, that’s what C’s functions are, globals (well, that and
file-statics) :wink: .> Date: Wed, 10 Jul 2013 22:50:46 +1000

From: john skaller
To: SDL Development List
Subject: Re: [SDL] Thread local storage API
On 10/07/2013, at 10:34 PM, Sik the hedgehog wrote:

Date: Thu, 11 Jul 2013 05:00:08 +1000
From: john skaller
To: SDL Development List
Subject: Re: [SDL] Thread local storage API
Message-ID:
<5CDA3552-B72B-484B-AE5F-1A0005D86B92 at users.sourceforge.net>
Content-Type: text/plain; charset=us-ascii

On 11/07/2013, at 2:42 AM, Alexander Sabourenkov wrote:

When one desperately wants to shoot himself, he will settle with just
a foot, if the framework won’t allow him to blow his head off and be
done with it. So why bother? Not forcing shot feet and crutches on
people is good enough.

The way I see it is that programming languages are there to support
good design. Otherwise we’d just write hex codes (like I used to
in the old days :slight_smile:

Programming languages exist to make it easier to program. Sometimes
this means protecting you from yourself (any pure-functional language,
I would assume), sometimes this means giving you the firepower to
shoot the other side of the planet via your foot (C, C++), sometimes
this means keeping you within certain design constraints. It all
varies.

Date: Thu, 11 Jul 2013 06:02:02 +1000
From: john skaller
To: SDL Development List
Subject: Re: [SDL] Thread local storage API
Message-ID:
<6E7E70A1-2C9B-4DA8-BAAD-99DBB0EA6A0E at users.sourceforge.net>
Content-Type: text/plain; charset=us-ascii

On 11/07/2013, at 5:14 AM, Sik the hedgehog wrote:

Technically SDL uses return values to indicate when a function failed.

So does Posix. But it doesn’t indicate what kind of error it is.

That’s the only thing you should pay attention to if you care about
error handling,

That’s not so if the kind of error matters.

and that’s even better than the stack because it
doesn’t even need to touch RAM (it can stay entirely within CPU
registers).

In principle, that’s the stack (auto storage in C).
The use of registers for the top of the stack is an optimisation.
Many CPU’s have no such registers (eg SPARC uses a register
file on the stack and relies on the cache instead. The TI 990
only has ONE register and this is one of the best designs ever).

SDL_GetError merely shows the same error as a string explaining what
happened.

Ah yes. So adding the error code as a parameter would and returning the
error code would be the way to go:

int error = function arguments;
if (error) {
char *estring = SDL_GetErrorString (error);
printf (“Error %s\n”, estring);
exit(1);
}

This code is re-entrant. Even better:

char *estring = SDL_GetUTF8ErrorString (language, error);

so now the language is provided as a key was well, which makes
internationalisation much easier. [Do not use C locales they’re
not re-entrant … broken design again]

Unless you’re writing a multi-seat app (which admittedly might be
relevant: never discount the ability of big-iron to stay efficient)
you wouldn’t need the language key, just a way to set the current
language (which admittedly might be what you mean by non-reentrant).

And no, we can’t get rid of SDL_GetError because the ABI was frozen.
Backwards compatibility is more important here.

It is unfortunate that backwards compatibility ensures design bugs
are immortal :slight_smile:

The way to do this is clearly mark bad features DEPRECATED
and then actually remove them in the next version.

The last time I suggested this route, I said 2-4 versions down the
line, instead of one. Move the declarations to another file that gets
included where they’re supposed to be and drop them from the
documentation, and most people won’t even realize they exist, making
it much easier to drop them later.

Breaking bad code is important. It has to be done or the whole project
gets locked into an untenable situation where it cannot be upgraded.

Continuous pain is better than sudden death :slight_smile:
IMHO of course.

Don’t forget sudden pain: the sensation of a function suddenly
disappearing after you’ve had an app in the wild that uses it for
several years. The pain to the SDL codebase isn’t the only issue here.

Date: Wed, 10 Jul 2013 15:13:31 -0700 (PDT)
From: Mason Wheeler
To: SDL Development List
Subject: Re: [SDL] Thread local storage API
Message-ID:
<1373494411.31210.YahooMailNeo at web122504.mail.ne1.yahoo.com>
Content-Type: text/plain; charset=“iso-8859-1”

Quick question.? Let’s say that someone is using SDL with Delphi, which
implements thead-local storage as a language feature and has some data (for
exception handling) already set up to use TLS as part of the runtime.? Will
having another system that tries to implement TLS loaded into the same
process end up clashing with it and breaking stuff?

Shouldn’t (at least under Windows, but is Delphi currently available
anywhere else?), but valid question. It really just depends on how
it’s done. If Delphi somehow garbage collects all TLS then that could
cause some sort of access violation. If SDL just assumes a particular
TLS slot is always available then that could do much the same. If
neither of those is true, then you should be fine.

2013/7/11, Jared Maddox :

Used correctly, globals have no reason to be a design fault: after
all, that’s what C’s functions are, globals (well, that and
file-statics) :wink: .

Pretty obvious we’re just talking about variables >.> (and there are
languages that forbid functions in the global namespace, so even
that’s debatable)

This should be fixed in the latest update.

Thanks!On Wed, Jul 10, 2013 at 8:03 AM, Rainer Deyke wrote:

On 10.07.2013 11:42, Sam Lantinga wrote:

FYI, I just added a thread local storage API to the SDL RC build, in
SDL_thread.h:
SDL_TLSCreate(), SDL_TLSSet(), SDL_TLSGet()

Please let me know if you run into any problems with it!

There appears to be a potential infinite recursion bug in SDL_TLSSet:
SDL_TLSSet calls SDL_SetError, SDL_SetError calls SDL_GetErrBuf, and
SDL_GetErrBuf calls SDL_TLSSet.


Rainer Deyke (rainerd at eldwood.com)

_____________**
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

Used correctly, globals have no reason to be a design fault: after
all, that’s what C’s functions are, globals (well, that and
file-statics) :wink: .

Please excuse my imprecision. There’s nothing wrong with
global constants. My concern is with global variables.

Programming languages exist to make it easier to program. Sometimes
this means protecting you from yourself (any pure-functional language,
I would assume), sometimes this means giving you the firepower to
shoot the other side of the planet via your foot (C, C++), sometimes
this means keeping you within certain design constraints. It all
varies.

However this seems to suggest a relativism and that is not viable.
Whilst programming does involve psychology it also involves mathematics.On 11/07/2013, at 1:45 PM, Jared Maddox wrote:


john skaller
@john_skaller
http://felix-lang.org

2013/7/10, john skaller :

Unless you have worked on the TI 990, how is your comment of any value?
Lack of registers is a really GOOD thing if you’re designing a processor
for
fast context switching. The TI990 was able to run 60 terminals on a
16 bit machine.

I know it makes it much easier on the hardware end (it becomes a lot
simpler), but on the software end it becomes annoying rather quickly.
Also I’m not sure how well would out of order execution work without
registers (it’d be probably doable, but at what cost?).

Agreed. But it doesn’t prevent you doing that, it just provides
an easy way to get a message if you want one, perhaps useful
during prototyping.

The problem is when libraries provide it as the only method, which
seems to be the case in practically all of them when they provide this
functionality. It’s like they want the program to bail out immediately
and do everything on its own instead of letting the program handle it
when possible. That’s a bad thing.

I think the only library I’ve seen that does both is, curiously, the C
standard library (it has strerror for a string but there’s also errno
for the actual error code).

No, its about SDL so it seems on topic for the list if not the Subject.

Not relevant to TLS though.

If I understand what you’re talking about (please correct me if I’m wrong)
the fact that in SDL you have to poll for events rather than just supply
callbacks is the main reason I like SDL. In fact if it had a run loop
I would not be able to use it at all.

Polling for events is still resorting to a main loop, even if it’s
your own loop.

Callback based code is utterly evil. It isn’t possible to write code
inside out like this very well. In fact this is what my language compiler
actually does: it takes “thread based” code and turns it inside out
behind the scenes to make it callback driven. The whole purpose
of the design is to save the programmer from callbacks and give
them back the “stack” so they can couple data and state in a sane
way.

But sadly things seem to be moving to become callback-driven. This is
true even for languages, functional languages outright go against the
idea of main loops, at least in an intuitive way.

I’m not saying it’s superior (the fact I mentioned working on assembly
should be a dead giveaway I’m more accustomed to sequential code), I’m
saying the underlying systems aren’t being represented accurately by
SDL.

Only if I don’t want to use some add on someone wrote that uses it.
Whereas if it isn’t there they can’t :slight_smile:

If they can’t do it the easy way they’ll go with a horrible hack.
Don’t underestimate programmers.

Reminds me of how HTML5 included an AUTOPLAY attribute to media
elements despite directly going against accessibility, because
otherwise everybody would implement it through scripting instead
(while as an attribute the browser can easily turn it into a setting).

I know it makes it much easier on the hardware end (it becomes a lot
simpler), but on the software end it becomes annoying rather quickly.

I do not understand, it has no impact on software at all.

Also I’m not sure how well would out of order execution work without
registers (it’d be probably doable, but at what cost?).

On the TI990 and also I believe on the SPARC there is a register file
pointed at by the context pointer. Instructions therefore access memory,
but only a limited amount of it, and a small ultra-fast cache takes
care of performance.

This is similar to a stack machine, which typically has only
two registers PC and SP. The top of the stack is accessed
frequently and so migrates to the fastest cache.

In the TI 990 the PC and SP are in the register file,
however there is a special single word cache for the PC
in the chip. The CP is the only register in the CPU, it points
to the register file.

In these machines context swap is a single machine instruction.
This is hundreds of times faster than possible on x86_64, for example.

If I understand what you’re talking about (please correct me if I’m wrong)
the fact that in SDL you have to poll for events rather than just supply
callbacks is the main reason I like SDL. In fact if it had a run loop
I would not be able to use it at all.

Polling for events is still resorting to a main loop, even if it’s
your own loop.

No, it isn’t. You should study some fibrated code in Felix to
understand. This code typically contains LOTS of loops
and no main loop at all.

As a mental example: consider a GUI where every widget
did a READ operation to get events relevant to it.

This is the control inverse of every widget being CALLED with
the relevant event data.

A related example: a typical Unix process pipeline.
Each process reads from its input pipe and writes to
its output pipe. Each process has a loop. But there
is no main loop in the complete application code.

There IS of course a main loop here … but it is in the
Operating System, not the application.

I’m not saying it’s superior (the fact I mentioned working on assembly
should be a dead giveaway I’m more accustomed to sequential code), I’m
saying the underlying systems aren’t being represented accurately by
SDL.

That depends what “level” of the underlying system you’re talking
about doesn’t it?

Only if I don’t want to use some add on someone wrote that uses it.
Whereas if it isn’t there they can’t :slight_smile:

If they can’t do it the easy way they’ll go with a horrible hack.
Don’t underestimate programmers.

LOL. Point taken. I should know I’m the master of hacks :slight_smile: :-)On 11/07/2013, at 6:21 PM, Sik the hedgehog wrote:


john skaller
@john_skaller
http://felix-lang.org

My reply to this thread was full of off-topic stuff, so I’ve cut it
down quite a bit. Since it’s off-topic, please send any replies to me
directly.> Date: Thu, 11 Jul 2013 02:00:13 -0300

From: Sik the hedgehog <sik.the.hedgehog at gmail.com>
To: SDL Development List
Subject: Re: [SDL] Thread local storage API
Message-ID:
<CAEyBR+VF9NZOq9c=vZ0_K_Sciy9wHCXFY5B-V4RhUsD0UHg9CA at mail.gmail.com>
Content-Type: text/plain; charset=UTF-8

2013/7/11, Jared Maddox <@Jared_Maddox>:

Used correctly, globals have no reason to be a design fault: after
all, that’s what C’s functions are, globals (well, that and
file-statics) :wink: .

Pretty obvious we’re just talking about variables >.> (and there are
languages that forbid functions in the global namespace, so even
that’s debatable)

I know. I was pointing out that globals shouldn’t be automatically
opposed. Including global variables.

Date: Thu, 11 Jul 2013 17:58:25 +1000
From: john skaller
To: SDL Development List
Subject: Re: [SDL] Thread local storage API
Message-ID:
<0EBF5048-7D69-42C0-A088-D84A98079FD4 at users.sourceforge.net>
Content-Type: text/plain; charset=us-ascii

On 11/07/2013, at 1:45 PM, Jared Maddox wrote:

Used correctly, globals have no reason to be a design fault: after
all, that’s what C’s functions are, globals (well, that and
file-statics) :wink: .

Please excuse my imprecision. There’s nothing wrong with
global constants. My concern is with global variables.

Technically speaking, I think those have their place as well, though
as far as I can see, singletons such as backbone pieces for libraries
are the only good place for them.

Programming languages exist to make it easier to program. Sometimes
this means protecting you from yourself (any pure-functional language,
I would assume), sometimes this means giving you the firepower to
shoot the other side of the planet via your foot (C, C++), sometimes
this means keeping you within certain design constraints. It all
varies.

However this seems to suggest a relativism and that is not viable.
Whilst programming does involve psychology it also involves mathematics.

Relativism in what sense? If it’s in the sense that I think “safety"
should depend on the language’s goals, then yes. I have my
disagreements with C (as far as the core language goes, with
declaration & pointer syntax, and especially the overlap of those
two), but I think that FOR IT’S FIELD it’s mostly right, because it
favors minimalism first. C++ I disagree with more, but mostly because
the language has turned into a mess and could really benefit from a
"reboot”. The moment you get into mandatory pause-the-world garbage
collection I’m inclined to veto a language’s use for anything where
performance maters, e.g. UI. The moment you drop pointers (or an easy
alternative) I’m inclined to relegate a language to either the "toy"
realm, or the “pure-functional” realm.

Various things have their places, but in the realm of programming, I
hold that certain things should be considered mutually exclusive (e.g.
heavy calculations don’t belong in the same thread as your GUI).

I’m looking into wrapping this functionality in a C++ class. However, I’m missing a SDL_TLSDestroy? Looking at the simplicity of SDL_TLSCreate it’s not trivial to add this functionality.

Any thoughts?

  • Michiel

Code:

class TLS
{
public:
typedef void Destructor(void*);
inline TLS(Destructor* destructor = NULL)
{
mId = SDL_TLSCreate();
mDestructor = destructor;
}

inline ~TLS() { /* todo? */ }

inline void* get() { return SDL_TLSGet(mId); }
inline void set(void* object) { SDL_TLSSet(mId, object, mDestructor); }

private:
SDL_TLSID mId;
Destructor* mDestructor;
};------------------------
Cheers from the Piko3D team!

http://www.piko3d.net

Message-ID: <1394481509.m2f.42502 at forums.libsdl.org>
Content-Type: text/plain; charset=“iso-8859-1”

I’m looking into wrapping this functionality in a C++ class. However, I’m missing a SDL_TLSDestroy? Looking at the simplicity of SDL_TLSCreate it’s not trivial to add this functionality.

Any thoughts?

  • Michiel

Code:

class TLS
{
public:
typedef void Destructor(void*);
inline TLS(Destructor* destructor = NULL)
{
mId = SDL_TLSCreate();
mDestructor = destructor;
}

    inline ~TLS() { /* todo? */ }

    inline void* get() { return SDL_TLSGet(mId); }
    inline void set(void* object) { SDL_TLSSet(mId, object, mDestructor); }

private:
SDL_TLSID mId;
Destructor* mDestructor;
};

Just to make sure, you’re wanting users to be able to use this to
register a function that will be called when the instance of TLS that
they’ve created is destroyed, correct? Bearing in mind the TLS memory
might be a rare commodity on some platforms (I can’t think of anything
current, but I think that pre-WinXP had maybe 16 pointer slots that
you could use), maybe something more like this:

class TLSCore
{
public:
typedef void Destructor( void* );
typedef std::map< std::uintptr_t, std::pair< void*, Destructor* >

DestructorMap;
private:
static std::list< SDL_threadID, SDL_TLSID > mapIds;

static extern "C" void deleteDestructorMap( void *destructorMap )
{
    DestructorMap *dMap = static_cast< DestructorMap* >( destructorMap );
    DestructorMap::iterator iter = dMap->begin();

    while( iter != dMap->end() )
    {
        *( iter->second->second )( iter->second->first );
        ++iter;
    }

    delete dMap;
}

public:
inline void TLSAdd( std::uintptr_t key, Destructor *destructor = NULL )
{
DestructorMap *dMap;

    if( mapIds.find( SDL_ThreadID() ) == mapIds.end() )
    {
        SDL_TLSID id = SDL_TLSCreate();

        dMap = new DestructorMap();

        SDL_TLSSet( id, static_cast< void* >( dMap ),

&deleteDestructorMap );
mapIds[ SDL_ThreadID() ] = id;

    } else {

        dMap = static_cast< DestructorMap* >( SDL_TLSGet( mapIds[

SDL_ThreadID() ] ) );
}

    ( *dMap )[ key ] = std::make_pair( static_cast< void* >( NULL

), destructor );
}

inline void* get( std::uintptr_t key )
{
    DestructorMap *dMap = static_cast< DestructorMap* >(

SDL_TLSGet( mapIds[ SDL_ThreadID() ] ) );

    return( ( *dmap )[ key ] );
}
inline void set( std:: uintptr_t key, void* object )
{
    DestructorMap *dMap = static_cast< DestructorMap* >(

SDL_TLSGet( mapIds[ SDL_ThreadID() ] ) );

    ( *dmap )[ key ] = object;
}

};

Thus, TLS becomes:

class TLS
{
public:
typedef void Destructor( void* );
inline TLS( Destructor *destructor = NULL )
{
core.TLSAdd( static_cast< std::uintptr_t >( this ), destructor );
}

inline void* get() { return( core.get( static_cast< std::uintptr_t

( this ) ) ); }
inline void set( void* object ) { core.set( static_cast<
std::uintptr_t >( this ), object ); }

private:
static TLSCore core;
};> Date: Mon, 10 Mar 2014 19:58:29 +0000

From: “Paramike” <michiel.roza at gmail.com>
To: sdl at lists.libsdl.org
Subject: Re: [SDL] Thread local storage API

Just to make sure, you’re wanting users to be able to use this to
register a function that will be called when the instance of TLS that
they’ve created is destroyed, correct? Bearing in mind the TLS memory
might be a rare commodity on some platforms (I can’t think of anything
current, but I think that pre-WinXP had maybe 16 pointer slots that
you could use), maybe something more like this:

Fwiw, SDL uses one OS-level TLS slot, and then manages its own slots on
top of it. So short of running out of address space, you basically have
infinite TLS slots in SDL.

–ryan.