boss.tcl010066400017560001750000000035150740112335500130470ustar00challorbis00002730000003package require Thread

set N_THREADS 5
set N_TASKS   10

set T_PTR     0
set T_LIST    [list]


#
# Create single logging thread - don't have to do this, but just
# for illustration...
#
set logger [thread::create {source logger.tcl}]

#
# Create artificial list of tasks
#
for {set i 0} {$i < $N_TASKS} {incr i} {
	lappend T_LIST $i
}

#
# Create and configure set of worker threads
#
for {set i 0} {$i < $N_THREADS} {incr i} {

	set t [thread::create -joinable {source worker.tcl}]

	#
	# Define values for logger and boss thread ids in the worker
	#
	thread::send -async $t [list config logger $logger]
	thread::send -async $t [list config boss   [thread::id]]

	#
	# Tell the worker to get started
	#
	thread::send -async $t [list start]
}

set joins_left $N_THREADS
set finished   0
set joinable   [list]


#
# Handler for task requests from worker threads - if there are remaining
# tasks on the list, give out a task, otherwise tell the thread that the
# job's done
#
proc next_task {tid} {

	global T_LIST
	global T_PTR
	global N_TASKS

	if {$T_PTR < $N_TASKS} {
		set TASK [lindex $T_LIST $T_PTR]
		thread::send -async $tid [list handle task $TASK]
		incr T_PTR
	} else {
		thread::send -async $tid [list stop]
	}
}


#
# Notification from a worker that it is exiting - add the worker thread
# id to the list of threads we will join later (below) - when we have no
# more threads left to exit, we set the "finished" variable which will
# make the vwait (below) return
#
proc worker_exit tid {
	global joins_left finished joinable
	incr joins_left -1
	lappend joinable $tid
	puts "BOSS: worker_exit $tid"
	if {$joins_left == 0} {
		set finished 1
	}
}

vwait finished

puts "BOSS: done..."

#
# All done so terminate the logger - use "-async" to avoid begin told that
# the thread died while this call is in progress...
#
thread::send -async $logger terminate
worker.tcl010066400017560001750000000020240740112331400133770ustar00challorbis00002730000003array set CONFIG [list]

set ntasks 0

#
# Handler for setup information from the boss
#
proc config {name val} {
	global CONFIG
	set CONFIG($name) $val
}

#
# Handler for thread start - when told to start, we ask the boss
# for a task
#
proc start {} {
	global CONFIG
	thread::send -async $CONFIG(boss) [list next_task [thread::id]]
}

#
# Handler for stop messages - call thread::unwuind which will break us out
# of the wait section (below)
#
proc stop {} {
	thread::unwind
}

#
# Handler for a task - just log some info and get the next task - note that
# we're doing all this stuff with -async
#
proc handle {args} {
	global CONFIG
	global ntasks
	incr ntasks
	thread::send -async $CONFIG(logger) [list log task_done [thread::id]]
	after [expr {int(rand()*100)}]
	thread::send -async $CONFIG(boss)   [list next_task [thread::id]]
}

#
# This is the thread "event loop"
#
thread::wait

set tid [thread::id]

thread::send -async $CONFIG(boss)    "worker_exit $tid"
thread::send -async $CONFIG(logger)  "log $tid exiting..."

#return $ntasks
logger.tcl010066400017560001750000000001340734271142700133610ustar00challorbis00002730000003proc log {args} {
	puts stdout $args
}

proc terminate {} {
	thread::unwind
}

thread::wait
