Tcl Source Code

View Ticket
Login
Ticket UUID: 674367
Title: call for a function that is not thread safe
Type: RFE Version: None
Submitter: elfring Created on: 2003-01-24 23:39:57
Subsystem: 80. Thread Package Assigned To: vasiljevic
Priority: 5 Medium Severity:
Status: Open Last Modified: 2003-11-09 23:26:00
Resolution: None Closed By:
    Closed on:
Description:
I describe an use case for the handling of unsafe functions calls with 
the thread package.

I must assume that an application 
programming interface (like a graphics library or a music API) or even 
a single function is not thread safe if this property is not described in the 
documentation for that API.
So I must ensure that all unsafe 
functions are executed by a single thread at each time. I want to create a 
thread for each function library that must be handled as it has not 
implemented thread safety at startup/initialization time of my 
application. I want to achieve that calls into different unsafe libraries 
can be executed simultaneously.

I can perform the call with the 
function "thread::send" for a thread that has been created before. But I 
think that I will need a special kind of mutual exclusion. I understand the 
thread package's documentation in the way that a mutex that is created 
by the command "thread::mutex create" belongs to the calling thread. 
It may not be stored in a thread shared variable. Please correct me if I 
make wrong assumptions here.

I guess that the function 
"thread::eval" does not all what I want and need here. It does not expect 
a thread indentifier in its argument list.
I need a mutex that is global to 
the process scope 
(http://citeseer.nj.nec.com/cs?cs=1&q=synchronization+and+global+and+mutex&submit=Documents&co=Expected+Citations&cm=50&cf=Any&ao=Expected+Citations&am=20&af=Any
) 
to perform a protected and synchronous call of a code piece that 
should be executed by an other thread.
I would like to suggest new 
functions like the following.

1. thread::mutex::create 
global
         or
   thread::mutex -global create

2. 
thread::safecall id global_mutex script ?varname?


How 
do you think about this suggestion?
Does this use case lead to add 
more synchronization primitives to the thread package?
User Comments: elfring added on 2003-11-09 23:26:00:
Logged In: YES 
user_id=572001

# Would this implementation have the same effect?

namespace eval monitored_executor \
{
namespace export do new delete

proc do {id work} \
 {
  return [thread::send $id $work]
 }

proc new {} \
 {
  return [thread::create -preserved]
 }

proc delete (id) \
 {
  thread::release $id
 }
}

# Did my last point of view from C/C++ make it harder to see the TCL functionality?
# Can the requested option "-lock" help if the function "send" is synchronously and completely processed?

elfring added on 2003-11-09 02:24:46:

File Added - 66887: MonitoredExecutor.zip

Logged In: YES 
user_id=572001

I have added a TCL file.
I hope that this source code variant can help to express my ideas and concepts on this topic.

elfring added on 2003-11-09 02:21:11:

File Deleted - 66706:

elfring added on 2003-11-07 04:25:22:

File Added - 66706: MonitoredExecutor.zip

elfring added on 2003-11-07 04:23:36:

File Deleted - 66690:

elfring added on 2003-11-07 01:30:28:

File Added - 66690: MonitoredExecutor.zip

Logged In: YES 
user_id=572001

I have published the uploaded file also in the forum "class for function calls that are not thread safe" (http://groups.google.de/groups?th=ee30cb7159ff86eb&group=comp.programming.threads).
Can it improve TCL source code?

elfring added on 2003-09-07 19:57:48:
Logged In: YES 
user_id=572001

1. Are you going to add this description to the 
documentation?

2. I am looking for a monitor implementation 
that has got the scope of a library or package and not a single 
synchronized method or block like it is an Java.
A function call will 
get this monitor as a parameter.

vasiljevic added on 2003-07-14 01:36:25:
Logged In: YES 
user_id=95086

Your assumption about mutexes is wrong. Mutex handles 
can be shared among threads since very nature of the 
mutex is to synchronize access from many threads. 
So, handles to mutexes created with tnread::mutex create 
can be left in thread shared arrays and accesed from  
many threads simultaneously. The thread package contains 
examples of Tcl code (web server and command server) where 
sync techniques with mutexes and condition variables are 
employed. 
 
Zoran

elfring added on 2003-07-13 23:08:16:
Logged In: YES 
user_id=572001

Example:
It is described on the page 
"http://tcljava.sourceforge.net/docs/website/getstart.html" how 
the function "eval" must be protected against multithreaded calls.

elfring added on 2003-07-13 20:04:33:
Logged In: YES 
user_id=572001

I hope that my requests 
"https://sourceforge.net/tracker/?func=detail&aid=770193&group_id=7586&atid=357586" 
(Boost C++) and 
"https://sourceforge.net/tracker/?func=detail&aid=712663&group_id=7232&atid=357232" 
(ZThread) will help for this topic.

elfring added on 2003-06-24 22:41:27:
Logged In: YES 
user_id=572001

I assume that the requested function can be implemented 
with condition variables.
But I see an important detail. - The called thread should 
perform a "thread::cond wait" for the calling thread.
The caller will be woken up after the sent script was executed.

The scheduling strategy for the calling threads will be an 
interesting part in the solution.

elfring added on 2003-05-11 22:08:56:
Logged In: YES 
user_id=572001

Where and when will our discussion be continued?
Here or at the mailing 
list (more synchronization primitives for the threads package - 
https://sourceforge.net/mailarchive/forum.php?thread_id=1585197&forum_id=24972)?

elfring added on 2003-01-28 03:38:37:
Logged In: YES 
user_id=572001

1. Please add it. I like because it seems to be exatly the enhancement I 
want.

2. Please add this to the documentation.

3. Yes. Let us 
continue the discussion on the new mailing list "tcl-threads".

vasiljevic added on 2003-01-28 00:49:45:
Logged In: YES 
user_id=95086

1. There is none yet. It was just an idea to see if it would fit. 
2. Of course, Just make the catch 
  catch {thread::send $tid $script} 
or examine return value of: 
  thread::send $tid $script result 
to be 1 
 
Note that "thread::send $tid $script result" is the same as  
you would do with: 
    catch {thread::send $tid $script} result 
 
BTW, do you mind going to the mail-list for some discussions 
as Donal suggested? When we come to some nice conclusion 
we (or you) might post the feature request/bug etc so we do 
not clutter the SF. It is much more easier to me to answer 
your questions over email.

elfring added on 2003-01-28 00:03:15:
Logged In: YES 
user_id=572001

Well, your last command seems to be very close to my wish.
1. I just do not read an option "-lock" for the 
function "thread::send" in the HTML documentation at the 
moment.

2. What will happen if the script that is executed this way by the 
other thread will "throw" an error?
   Can the error be caught?

vasiljevic added on 2003-01-27 21:15:05:
Logged In: YES 
user_id=95086

Well, returning of values from threads, using the thread::send 
is already damn-simple. I do not know how to make it simpler. 
It is either: 
 
  set result [thread::send $tid "set somevar 12"] 
or 
  thread::send $tid "set somevar 12" result.  
 
What can be simpler than this? 
 
If you want to eval the script in an atomic fashion 
in the other thread, one can perhaps say: 
 
  thread::send -lock myMutex $tid {open file.txt} result 
 
Would this satisfy your requirement?

elfring added on 2003-01-27 20:07:44:
Logged In: YES 
user_id=572001

1. Please add your better explanation to the documentation.

2. My intention is just to execute a single function calll under the 
lock protection.
But I think that I will not get the return code of the "unsafe" 
function "open" in my first version of the example. I will get the 
return value of the function call "thread::eval" instead. How do 
you think about it?

set $TID [thread::create -preserved]
tsv::set X myMutex [thread::mutex create]
thread::send $TID \
  {
   thread::eval -lock [tsv::get X myMutex] {catch {open Test.txt w} 
Test}
   return $Test
  } File
puts $File Hallo
close $File
thread::mutex destroy [tsv::get X myMutex]
thread::release $TID

Can the retrieval for return values from other threads made 
easier?
Does the second example show a use case that can be 
supported by a single function in the TCL threads API?

vasiljevic added on 2003-01-27 15:25:51:
Logged In: YES 
user_id=95086

This example will instruct the $TID thread to open the Test.tst 
file under lock. If you however want to both open and write 
to the file in atomic way, you should move the "puts $File" 
in the thread::eval block. 
 
Normally, your application will create some mutexes up front 
and store them in the shared variable. Those live a long as 
the app lives and are generally never destroyed. During 
runtime, threads come and go and they use the precreated 
mutexes for various sync tasks.

elfring added on 2003-01-27 05:16:24:
Logged In: YES 
user_id=572001

2. Or will the following code example do what I want?

set $TID 
[thread::create -preserved]
tsv::set X myMutex [thread::mutex 
create]
thread::send $TID \
  {
   thread::eval -lock [tsv::get X 
myMutex] {open Test.txt}
  } File
puts $File Hallo
close 
$File
thread::mutex destroy [tsv::get X myMutex]
thread::release 
$TID

elfring added on 2003-01-26 21:07:54:
Logged In: YES 
user_id=572001

1. And your explanation "... this way you can be sure
that only *one* 
thread at a time in the entire process can
run ..." is easier to understand 
than the current description "If no mutex is specified ... an internal static 
mutex is used". (http://cvs.sourceforge.net/cgi-
bin/viewcvs.cgi/*checkout*/tcl/thread/doc/html/thread.html)

elfring added on 2003-01-26 20:54:49:
Logged In: YES 
user_id=572001

1. Thanks for your answer.
    Please add to the documentation that the TCL 
mutexes have got global scope.
   (The mutex identifiers should not be 
stored in local variables. They must be stored in thread shared variables to be 
useable by all threads.)

2. But I think that something is still missing for 
the suggested function "thread::safecall id tsvname script 
?varname?".
I want that a different thread does something with 
"thread::eval -mutex". How will the calling thread get the return values from the 
called thread?

3. Will the (missing) sychronization primitives that 
are described in the chapter "2.3. Interprocess Communication" of the 
book "Modern Opreation Systems" 
(http://www.cs.vu.nl/~ast/books/mos2/) be added to the thread package?

vasiljevic added on 2003-01-25 16:57:48:
Logged In: YES 
user_id=95086

Mutexes created with "thread::mutex create" are  
already in the global scope. 
They can be used from any thread. A mutex tied to a thread 
has no real benefit. So you're perfectly ok with that. 
The mutex indentifier returned by "thread::mutex create" can 
be passed to another thread or stored in the thread shared 
variable (array) and used from other threads as well. 
 
Also, thread::eval is a nice shortcut of 
 
  thread::mutex lock $mutex 
  # do some Tcl work 
  thread::mutex unlock $mutex 
 
so you can just say: 
 
  thread::eval -mutex $mutex { 
    # do some Tcl work 
  } 
 
The benefit is: you can never forget to unlock the 
mutex. Also, In case of script error, the implementation 
guarantees that the mutex will be unlocked. Without this 
you must take care yoursefl about unlocking the mutex 
which may prove complicated in some cases. 
A very simplified use of thread eval is: 
  thread.:eval { 
    # do some Tcl wor 
  } 
 
where the mutex is implicit and this way you can be sure 
that only *one* thread at a time in the entire process can 
run the Tcl code.

elfring added on 2003-01-25 16:10:09:
Logged In: YES 
user_id=572001

Values are only available globally with TCL threads if the data is stored in 
thread shared variables. (http://cvs.sourceforge.net/cgi-
bin/viewcvs.cgi/*checkout*/tcl/thread/doc/html/tsv.html).

So I 
adjust my suggestion for the function interface design. The identifier for the 
mutex object with the global scope must be accessed by a thread shared 
variable.

1. thread::mutex::create ?tsvname 
...?

2.
thread::safecall id tsvname script ?varname?

Attachments: