On 10/4/06, Zoran Vasiljevic <[EMAIL PROTECTED]> wrote:
On 04.10.2006, at 18:44, Stephen Deasey wrote:
>>
>> OK. I can accept that. Still, you put this into frequently
>> called procedure and there you go (literals are freed AFAIK,
>> when procedure scope is exited): again you have global locking.
>
>
> I'm not sure what you're saying here. What is freed, and when?
>
The literal object "themutex" is gone after procedure exits
AFAIK. This MIGHT need to be double-checked though as I'm
not 100% sure if the literals get garbage collected at that
point OR at interp teardown.
If that were true then my scheme would be useless. But I don't think
it is. I added a log line to GetArgs in nsd/tclthread.c:
if (Ns_TclGetOpaqueFromObj(objv[2], type, &addr) != TCL_OK
&& Ns_TclGetAddrFromObj(interp, objv[2], type, &addr) != TCL_OK) {
Ns_Log(Warning, "tclthread.c:GetArgs: objv[2] not an
address object. Looking up in hash table...");
Tcl_ResetResult(interp);
Ns_MasterLock();
...
i.e. if the 'name' of the thread primitive given to the command does
not already contain a pointer to the underlying object, and it can not
be converted to one by deserialising a pointer address, then take the
global lock and look the name up in the hash table. Log this case.
% proc lockunlock args {ns_mutex lock m; ns_mutex unlock m}
% lockunlock
[04/Oct/2006:18:39:25][8985.3086293920][-thread-1208673376-] Warning:
tclthread.c:GetArgs: objv[2] not an address object. Looking up
in hash table...
% lockunlock
% lockunlock
The first time the proc is run it is compiled and the name is indeed
looked up in the hash of mutex names. And that is of course 'slow'
because there's a lock around the hash table.
But that is the *only* time this happens. On the second and third
attempt, no locking or look up!
What's interesting from the above is that there is only a single log
line, but there are two literal "m" objects. Apparently Tcl is doing
some optimising behind the scenes...
> Right. But why would you ever do this? This was a corner case example
> I gave to show that it *could* happen, if you tried real hard and
> looked at it funny, but you wouldn't actually do this, right?
Correct.
>
>
>> Unlike with handles
>> where you get the "real thing" immediately and need not global
>> lock.
>
>
> In the case of the thread objects this is the case, but they are
> special (or weird). The internal rep of a thread object (mutex, cond
> var etc.) is a serialised C pointer. Given the string you can
> re-create the pointer (and if you try hard enough you can create an
> invalid pointer and crash the server, hence weird).
>
> But for handles in general, such as nsproxy, this is not the case.
>
> Remember, if you can refer to thread objects by name then you don't
> *need* to put the name in an nsv array, for example. And if you do,
> ns_mutex create does in fact still return a handle.
BUT: to be able to refer them by name I need a lookup table.
And if this is to be thread-wide I need to lock that table globally.
But only ONCE! The look up is cached.
>> Keeping the handles "away" from the user, means you need to manage
>> handles yourself because the underlying C code needs handles/
>> pointers.
>> Every time you do that, you need to lock. In this case the global
>> lock.
>
>
> But you don't. That's the point. If this isn't the case, then I've
> done something wrong and the code needs to be reverted.
You don't?? You do!
ns_mutex create
returns handle. But I can say:
ns_mutex lock junklock
and it will *hide* a newly created mutex and tag it with
a "junkmutex" in a thread-wide table. There you go.
The user never "see's" that real mutex handle. He knows
only "junklock". With some object tricks you "remember"
the real handle so to avoid lookup in a locked table.
But when this shimmers away, you're out of bussiness.
Right. But it won't shimmer away, because it is a literal name and you
have no need to manipulate it in any way that would cause it to
shimmer, such as putting it in an nsv array.
Now, I'm not sure what you're getting at with the "junklock" business.
If you mean the user could have a typo in their code and an extra lock
will be created behind their back, well, that's the nature of Tcl.
Same goes for variables, right?
Although you could define ns_mutex create to take a name and force
people to use this in their initialisation code, and then in calls to
lock and unlock you wouldn't create on demand, you'd just do the look
up (first time only!), and throw an error if the name doesn't exist.
But maybe I'm missing your point here...
OTOH, when I say
set mutexhandle [ns_mutex create]
I can do whatever I want with mutexhandle, it will always point to
that damn mutex. I need not do some other lookup in addition.
I can put it into nsv, transfer it over network back to myself...
It's not that you *can* put it into an nsv array, it's that you *have*
to, because how else are you going to communicate the serialised
pointer which is a mutex handle?
And you *have* to do it because what good is a mutex without more than
one thread?
And if you have more than one thread you have more than one interp,
each with it's own global namesapce. So how do you refer to a single
mutex from within each interp?
Thats what I mean by removing the handle from users. If you
do that you need to do more, introduce more locks etc.
Nothing is for free...
It is in fact more or less free. Compiling the Tcl source to byte code
incurs a lock around the mutex hash table. It is a compile time
expense. At run time there is no locking.
nsv arrays *also* incur locking. Plus you have the effort of having to
cache the mutex in a thread local variable, or else you incur the nsv
locking cost each time.
So I think this is in fact a case of a free lunch!
As far as I can see, the only thing that will make this not true, is
some real-world, non-contrived case where the mutex name has to
shimmer.