Joost has asked for the wisdom of the Perl Monks concerning the following question:

Hello Monks.

Currently I'm working on a XS/C++ system on linux w/ pthreads. Many of the objects in the system are intended to be shared across perl threads (for various reasons - but mainly memory efficiency).

My question now is: assuming an object is shared, is there any way to know in how many perl threads it's referenced?

This is important to know, because apparently if a shared object goes out of scope in one thread, the default behavior is to call DESTROY() on it, which calls delete(), which makes the object unusable for any other threads that are still running.

Currently I'm doing something like this (ThreadCounted is a base class for objects that are shared but need to be destroyed when they're out of scope in all threads):

pthread_mutex_t ThreadCounted::mutex = PTHREAD_MUTEX_INITIALIZER; unsigned int ThreadCounted::threadcount = 1; ThreadCounted::ThreadCounted() { pthread_mutex_lock(&mutex); threadrefs = ThreadCounted::threadcount; pthread_mutex_unlock(&mutex); } ThreadCounted::~ThreadCounted() { } void ThreadCounted::CLONE() { pthread_mutex_lock(&mutex); ThreadCounted::threadcount++; pthread_mutex_unlock(&mutex); } void ThreadCounted::DESTROY() { pthread_mutex_lock(&mutex); if (--threadrefs == 0) { delete this; } pthread_mutex_unlock(&mutex); }
In other words, I'm assuming the objects are shared across all threads, and the object is only deleted when the last thread has called DESTROY on it.

Also, I'm assuming the class is loaded when there's only one thread running, and the thread count is incremented every time CLONE() is called.

This has some disadvantages, mainly that (AFAIK) I can't tell when a thread is stopped (i.e. there's no hook corresponding to the "inverse" of CLONE). Also it's possible for an object to be shared only by a subset of the threads (again, AFAIK) and I won't be able to tell.

I'd appreciate any insights & comments.

Cheers,
Joost.

Replies are listed 'Best First'.
Re: threads, refcounting, XS, DESTROY
by Anonymous Monk on Aug 15, 2007 at 16:08 UTC

    An alternative, perhaps uglier, approach is to share your objects and check threads::shared::_refcnt during DESTROYction.

    POSIX::RT::Semaphore does this when run under threads, though a development branch did much the same CLONE counting as you detail above. It's all a lot of unfortunate bookkeeping, IMHO, to get the behavior exhibited by filehandles under threads, where the last dismissal in the last thread releases the underlying system resource.

      That may be uglier but it sounds like it would work a whole lot more robustly.

      Ps: is threads::shared::_refcnt() documented anywhere?

      update: I haven't found any documentation for this function, but a quick skimming of the source and tests for threads::shared indicates that threads::shared::_refcnt() returns the sum of the refcounts over all threads. very useful indeed. I'm going to experiment with this.

        Unfortunately, it appears that _refcnt() does not always report the expected value during DESTROYction, meaning, IIUC, that final cleanup might be missed using this technique:

        http://www.nntp.perl.org/group/perl.ithreads/2007/08/msg1199.html

        Looks like it's back to user-defined bookkeeping...