Google Groups no longer supports new Usenet posts or subscriptions. Historical content remains viewable.
Dismiss

Tcl_Obj and threads

23 views
Skip to first unread message

mat...@gmail.com

unread,
Oct 12, 2006, 3:00:35 AM10/12/06
to
I'm running a voip library (iaxclient) that produces callbacks from
worker threads and have a notifier that invokes them on the tcl
main thread using something like the code below. Problem is that
Duo Core owners report serious problems and I suspect the code below
to be the cause. From the single concrete bug report I have it looks
like the notifyObj sneaks in the tcl main interpreter and just
replaces variables?!

Probably I'm very naive here since I'm unused to thread coding.
Do the Tcl/Tk environment need to be thread enabled?

/Mats


static Tcl_Obj *notifyOb = NULLj;

/* From Tcl main thread: */

Tcl_MutexLock(&notifyRecordMutex);
if (notifyObj) {
Tcl_DecrRefCount(notifyObj);
notifyObj = NULL;
}
notifyObj = Tcl_DuplicateObj(objv[2]);
Tcl_IncrRefCount(notifyObj);
Tcl_MutexUnlock(&notifyRecordMutex);


>From worker thread created with pthread_create (or _beginthreadex):

Tcl_MutexLock(&notifyRecordMutex);
if (notifyObj) {
Tcl_Obj *cmdObj = Tcl_DuplicateObj(notifyObj);
Tcl_IncrRefCount(cmdObj);
Tcl_ListObjAppendElement(NULL, cmdObj, Tcl_NewIntObj(...));
...
/* Event posted to the Tcl main thread interpreter event queue
using
Tcl_GetStringFromObj(cmdObj, NULL) and Zoran's code at:
http://wiki.tcl.tk/15342
*/
Tcl_DecrRefCount(cmdObj);
}
Tcl_MutexUnlock(&notifyRecordMutex);

a...@piskorski.com

unread,
Oct 14, 2006, 3:48:32 AM10/14/06
to
mat...@gmail.com wrote:
> I'm running a voip library (iaxclient) that produces callbacks from
> worker threads and have a notifier that invokes them on the tcl
> main thread using something like the code below. Problem is that
> Duo Core owners report serious problems and I suspect the code below

> Probably I'm very naive here since I'm unused to thread coding.


> Do the Tcl/Tk environment need to be thread enabled?

Is that a trick question? If you're using multiple threads (probably
at all), and ESPECIALLY if you're using Tcl interpreters in multiple
threads, then YES, of course you MUST use a Tcl built with
"--enable-threads". Trying to use a single threaded Tcl for
multi-threaded work is definitely asking for very serious trouble.

Donald Arseneau

unread,
Oct 14, 2006, 7:40:04 AM10/14/06
to
"a...@piskorski.com" <a...@piskorski.com> writes:

> Trying to use a single threaded Tcl for
> multi-threaded work is definitely asking for very serious trouble.

I wouldn't say so. You need thread-enabled Tcl to do any
thready things from within Tcl, but any Tcl works nicely
for scripting within a big multi-threaded program. Each
thread creates its own Tcl interp, and they run well
independently and concurrently.


--
Donald Arseneau as...@triumf.ca

mat...@gmail.com

unread,
Oct 14, 2006, 9:33:46 AM10/14/06
to

a...@piskorski.com skrev:

I guess I have asked for serious trouble ;-)
There is only a single Tcl interpreter running on the main thread.
It is just an loadable extension that creates worker threads completely
unrelated
to Tcl, and I construct the callback commands with all arguments
from a worker thread using Tcl_Obj and Tcl_MutexLock etc.
But I guess it is here things go completely wrong since Tcl_MutexLock
defines to empty if the Tcl runtime wasn't built with threads enabled.
Instead I now do:

/* os-dependent macros, etc. From iaxclient. */
#if defined(WIN32) || defined(_WIN32_WCE)
#define MUTEX CRITICAL_SECTION
#define MUTEXINIT(m) InitializeCriticalSection(m)
#define MUTEXLOCK(m) EnterCriticalSection(m)
#define MUTEXUNLOCK(m) LeaveCriticalSection(m)
#define MUTEXDESTROY(m) DeleteCriticalSection(m)
#else
#define MUTEX pthread_mutex_t
#define MUTEXINIT(m) pthread_mutex_init(m, NULL) //TODO: check error
#define MUTEXLOCK(m) pthread_mutex_lock(m)
#define MUTEXUNLOCK(m) pthread_mutex_unlock(m)
#define MUTEXDESTROY(m) pthread_mutex_destroy(m)
#endif

but still is not sure if using Tcl_Obj code from a worker thread is
safe.
Perhaps I should do plain old sprintf(), strcpy(), strcat() instead?

/Mats

Donal K. Fellows

unread,
Oct 14, 2006, 10:49:59 AM10/14/06
to
mat...@gmail.com wrote:
> I guess I have asked for serious trouble ;-)

Well, I do wonder how much of this is going about stuff the hard way.

> There is only a single Tcl interpreter running on the main thread.
> It is just an loadable extension that creates worker threads completely
> unrelated
> to Tcl, and I construct the callback commands with all arguments
> from a worker thread using Tcl_Obj and Tcl_MutexLock etc.

OK, that's a somewhat supported mode of use. But use of Tcl_Obj
instances from any thread other than the one they're created in is not
supported. I've no idea if you can hack things so that they work;
that's not how Tcl_Objs are designed to be used. (If you had a threaded
Tcl build, you'd definitely be in trouble since we typically use a
per-thread object allocator.)

> But I guess it is here things go completely wrong since Tcl_MutexLock
> defines to empty if the Tcl runtime wasn't built with threads enabled.
> Instead I now do:

[...]


> but still is not sure if using Tcl_Obj code from a worker thread is
> safe.

It isn't at all safe, especially given that everyone is now moving over
towards a default threaded build anyway. Officially, the only safe way
to let an unthreaded Tcl know of some message from another thread is
Tcl_AsyncMark.

> Perhaps I should do plain old sprintf(), strcpy(), strcat() instead?

Sounds like a plan to me.

Donal.

mat...@gmail.com

unread,
Oct 16, 2006, 2:55:01 AM10/16/06
to
Donal K. Fellows skrev:

>
> OK, that's a somewhat supported mode of use. But use of Tcl_Obj
> instances from any thread other than the one they're created in is not
> supported. I've no idea if you can hack things so that they work;
> that's not how Tcl_Objs are designed to be used. (If you had a threaded
> Tcl build, you'd definitely be in trouble since we typically use a
> per-thread object allocator.)
>
> > But I guess it is here things go completely wrong since Tcl_MutexLock
> > defines to empty if the Tcl runtime wasn't built with threads enabled.
> > Instead I now do:
> [...]
> > but still is not sure if using Tcl_Obj code from a worker thread is
> > safe.
>
> It isn't at all safe, especially given that everyone is now moving over

I now use Tcl_DString instead to build up the callback script that
is sent to the Tcl interpreter in the main thread.
I've looked at the DString source code and I don't see anything
that is specific to Tcl interpreter or thread. Do you believe this is
OK?

> towards a default threaded build anyway. Officially, the only safe way
> to let an unthreaded Tcl know of some message from another thread is
> Tcl_AsyncMark.
>

It was Zoran Vasiljevic that wrote the part that sends a script to
the main interpreter using Tcl_ThreadQueueEvent() and
Tcl_ThreadAlert().
Seems it is more or less a stripped down from the threads extension.

/Mats

Donal K. Fellows

unread,
Oct 16, 2006, 6:53:17 AM10/16/06
to
mat...@gmail.com wrote:
> I now use Tcl_DString instead to build up the callback script that
> is sent to the Tcl interpreter in the main thread.
> I've looked at the DString source code and I don't see anything
> that is specific to Tcl interpreter or thread. Do you believe this is
> OK?

Yes, that looks fine to me. The only thing that might cause a problem
is if the memory allocator is per-thread, and even then only if the
memory is being freed from another thread to the one that allocated it.
(IIRC, threaded Tcl uses a per-thread memory allocator by default these
days, but if you're using an unthreaded Tcl build, that won't apply. If
you've got a threaded build, you'll have to be careful.)

> It was Zoran Vasiljevic that wrote the part that sends a script to
> the main interpreter using Tcl_ThreadQueueEvent() and
> Tcl_ThreadAlert().
> Seems it is more or less a stripped down from the threads extension.

Oh, it was Zoran? He knows exactly what he's doing and can provide
better help than I can.

Donal.

mat...@gmail.com

unread,
Oct 16, 2006, 7:57:10 AM10/16/06
to
Donal K. Fellows skrev:

> mat...@gmail.com wrote:
> > I now use Tcl_DString instead to build up the callback script that
> > is sent to the Tcl interpreter in the main thread.
> > I've looked at the DString source code and I don't see anything
> > that is specific to Tcl interpreter or thread. Do you believe this is
> > OK?
>
> Yes, that looks fine to me. The only thing that might cause a problem
> is if the memory allocator is per-thread, and even then only if the
> memory is being freed from another thread to the one that allocated it.
> (IIRC, threaded Tcl uses a per-thread memory allocator by default these
> days, but if you're using an unthreaded Tcl build, that won't apply. If
> you've got a threaded build, you'll have to be careful.)
>

It looks rougly like this:

static void EventLevels(struct iaxc_ev_levels levels)
{
Tcl_DString ds;
Tcl_DStringInit(&ds);
...
Tcl_DStringAppendElement(&ds, buf);
...
XThread_EvalInThread(sMainThreadID, Tcl_DStringValue(&ds), 0);
Tcl_DStringFree(&ds);
}

where the alloc/free is within the same thread (and function)
so I guess I'm safe now, even in a threaded build.
Thanks for all your help to an ignorant thread programmer.

Mats

George Peter Staplin

unread,
Oct 16, 2006, 4:35:33 PM10/16/06
to
Donal K. Fellows wrote:

> mat...@gmail.com wrote:
> It isn't at all safe, especially given that everyone is now moving over
> towards a default threaded build anyway. Officially, the only safe way
> to let an unthreaded Tcl know of some message from another thread is
> Tcl_AsyncMark.

And isn't that buggy itself? KBK and jenglish have expressed concerns
over its locking usage.

Donal K. Fellows

unread,
Oct 16, 2006, 7:06:16 PM10/16/06
to
George Peter Staplin wrote:
> And isn't that buggy itself? KBK and jenglish have expressed concerns
> over its locking usage.

I thought that was threaded builds that had the problem? (Non-threaded
builds can't hit the problem because they don't know how to lock
anything in the first place).

The real problem, as I understand it, is that Tcl_AsyncMark serves
several divergent purposes, some of which really ought to have been
done differently. Firstly, it should set a (C volatile) variable so
that the next time the target interpreter reaches a "safe" point and
calls Tcl_AsyncReady, it will process the defined callback. Secondly,
it may also need to trigger the thread containing the target
interpreter to stop whatever blocking syscall it is doing (i.e., a sort
of gentle interrupt "if it isn't too much trouble") with this second
thing being unnecessary in the truly single-threaded case since the
interrupt will have already been delivered as part of the signal that
caused the call to Tcl_AsyncMark in the first place. There are probably
other things omitted from this list.

Donal (pthreads and signals make my head ache!)

Kevin Kenny

unread,
Oct 16, 2006, 9:30:20 PM10/16/06
to
Donal K. Fellows wrote:
> The real problem, as I understand it, is that Tcl_AsyncMark serves
> several divergent purposes, some of which really ought to have been
> done differently. Firstly, it should set a (C volatile) variable so
> that the next time the target interpreter reaches a "safe" point and
> calls Tcl_AsyncReady, it will process the defined callback. Secondly,
> it may also need to trigger the thread containing the target
> interpreter to stop whatever blocking syscall it is doing (i.e., a sort
> of gentle interrupt "if it isn't too much trouble") with this second
> thing being unnecessary in the truly single-threaded case since the
> interrupt will have already been delivered as part of the signal that
> caused the call to Tcl_AsyncMark in the first place.

Uhm, not exactly.

You're right about the first: set a volatile variable that
Tcl_AsyncReady can find. But the second "interrupt, please, if
it isn't too much trouble," needs to work even in the single-
threaded case, because of the possibility that Tcl_AsyncMark
was called from a separate thread that *isn't* managed by
Tcl - that's supposed to be acceptable even in the "single
threaded" builds. The only blocking syscall that's really
supposed to be broken by Tcl_AsyncMark, as I understand it,
is the 'select' in the notifier, but I suppose that there are
a few other places where an EINTR would be mostly harmless.

In the multithread case, Tcl_AsyncMark is badly broken, because
it locks a mutex, and entirely lacks any kind of deadlock
protection against running in a signal handler for a thread
that already has the mutex locked. I'm afraid that I don't
have a good fix.

If someone with more urgent needs than mine wants to try
taking over as maintainer of the Unix notifier, I'd be happy
to step down. I signed up for it only after nobody else
wanted it; I don't feel a deep sense of ownership. (Fair
warning: the notifier is rather deep. It's tricky to
understand, not because it's badly commented, but simply because
what it does is subtle and difficult.)

--
73 de ke9tv/2, Kevin

Donal K. Fellows

unread,
Oct 17, 2006, 4:16:03 AM10/17/06
to
Kevin Kenny wrote:
> In the multithread case, Tcl_AsyncMark is badly broken, because
> it locks a mutex, and entirely lacks any kind of deadlock
> protection against running in a signal handler for a thread
> that already has the mutex locked. I'm afraid that I don't
> have a good fix.

I did say that pthreads and signals make my head ache with good reason!

Donal.

0 new messages