source: tol/tolsh/tolsh.vfs/lib/tolcomm/tolserver.tcl @ 1f82c01

1941-docker-image-to-testdevel
Last change on this file since 1f82c01 was 1f82c01, checked in by Liván Ramírez Dorta <livanr68@…>, 17 years ago

refs#873: Improve TolServer process structure
Using diferent arrays, one for tasks and another for the slave's state

git-svn-id: ​https://www.tol-project.org/svn/tolp/trunk@2055 be4846ae-f051-483a-831f-b64e7d08527f

  • Property mode set to 100644
File size: 16.1 KB
Line 
1###############################################################################
2#
3#  tolserver.tcl -- communication layer of tolsh when acting as a server.
4#
5#  Originally taken from:
6#
7#    - Network server application template: http://wiki.tcl.tk/8301
8#    - fconfigure : http://wiki.tcl.tk/1469
9#
10###############################################################################
11
12package provide tolserver 1.0
13
14package require tlogger
15
16###############################################################################
17#
18#  tolserver - namespace to encapsulate the server state and behaviour
19#
20###############################################################################
21namespace eval ::tolserver {
22
23  variable data
24 
25  array set data {
26    socket       ""
27    queue,id     0
28    queue,tasks  ""
29    queue,cancel ""
30  }
31}
32
33global shared_tasks
34set shared_tasks(request) ""
35set shared_tasks(init) ""
36set shared_tasks(server_init) 0
37
38global shared_state
39
40###############################################################################
41#
42#  is_implemented -- check is a given proc is implemented in the
43#  server namespace.
44#
45#  Arguments:
46#
47#    command - a possible command name
48#
49#  Return:
50#
51#    a true value if command is implemented in for the server
52#    namespace a false value in other case.
53#
54###############################################################################
55proc ::tolserver::is_implemented { command } {
56  llength [info command [namespace current]::$command]
57}
58
59###############################################################################
60#
61#  get_client_id -- return the client host identification host:port
62#
63#  Arguments:
64#
65#    chan - the client socket channel for which host identification is requested
66#
67#  Return:
68#
69#    A string "host:port" identifying the client host for which 'chan'
70#    is connected.
71#
72###############################################################################
73proc ::tolserver::get_client_id { chan } {
74
75  upvar \#0 ::tolserver::${chan}_data channel_data
76 
77  return "$channel_data(host):$channel_data(port)"
78}
79
80###############################################################################
81#
82#  log -- write a log message into the server log file
83#
84#  This proc write a message into the server log channel which is
85#  handled by tlogger
86#
87#  Arguments:
88#
89#    msg - the message to be written
90#
91###############################################################################
92proc ::tolserver::log { level msg } {
93 
94  ::tlogger::log "tolserver" $level $msg
95}
96
97###############################################################################
98#
99#  register_client -- Configure a client channel
100#
101#    This proc is the server listener (see server socket creation),
102#    and is invoked whenever a client open a socket connection to this
103#    server.
104#
105#  Arguments:
106#
107#    chan - channel assigned to the client just conneted
108#    host - host address from where the channel was connected
109#    port - port from where the channel was connected in the host
110# 
111###############################################################################
112proc ::tolserver::register_client {chan host port} {
113
114  # We want to read whole lines
115  #
116  fconfigure $chan -blocking 0 -buffering line
117
118  log debug [fconfigure $chan]
119
120  # Invoke a proc to notify us that a client has registered.
121  #
122  on_client_connected $chan $host $port
123 
124  # Set up a fileevent to handle input
125  #
126  fileevent $chan readable \
127      [namespace code "read_from_client $chan"]
128}
129
130###############################################################################
131#
132#  read_from_client -- Read a complete data buffer from client
133#
134#  This proc is an event handler triggered whenever some data is
135#  available from the client channel.
136#
137#  Arguments:
138#
139#    chan - client channel
140#
141###############################################################################
142proc ::tolserver::read_from_client { chan } {
143
144  if {![eof $chan]} {
145    # Something could be read
146    #
147    if {[gets $chan buffer] == -1} {
148      # Only handle complete lines
149      #
150      return           
151    }
152  } else {
153    # Close up the channel and notify us that a client was disconnected.
154    #
155    on_eof_channel $chan
156    return
157  }
158
159  # process the buffer of data
160  #
161  on_client_request $chan $buffer
162}
163
164###############################################################################
165#
166#  on_client_request -- Process a buffer of data read from client.
167#
168#  Given a request 'buffer' just read from a client channel 'chan' the
169#  corresponding command is invoked. A request buffer is a word command
170#  followed by its arguments. The word commands accepted are:
171#
172#    - PING       : check if I'm alive
173#    - TOLEVAL    : evaluate a tol expression.
174#    - RECONN     : reconnection request
175#    - TCANCEL    : the client wants to cancel a task due to timeout
176#    - SHUTDOWN   : shutdown this process server.
177#
178#  Arguments --
179#
180###############################################################################
181proc ::tolserver::on_client_request {chan buffer} {
182
183  # Log this request
184  #
185  log "notice" "on_client_request ([get_client_id $chan]) : $buffer"
186  set idrq "NOID"
187  if {![regexp {([^\s]+)\s(\d+)\s([^\s]+)(.*)$} $buffer ==> cmd size idrq str]} {
188    set reason "invalid request format in '$buffer'"
189    log critical $reason
190    on_bad_request $chan $idrq $reason
191    return
192  }
193  set str [string trim $str]
194  set rsize [string length $str]
195  if {$rsize!=$size} {
196    set reason "invalid argument size '$str'. Should be $size and it was $rsize"
197    log critical $reason
198    on_bad_request $chan $idrq $reason
199    return
200  }
201  switch -- [string toupper $cmd] {
202    PING {
203      on_ping $chan $idrq
204    }
205    TOLEVAL {
206        on_toleval $chan $idrq $str
207    }
208    RECONN {
209        on_reconnect $chan $idrq $str
210    }
211    SHUTDOWN {
212      on_shutdown $chan
213    }
214    TCANCEL {
215      on_cancel $chan $str
216    }
217    default {
218      on_bad_request $chan $idrq "unknown request '$cmd' in '$buffer'"
219    }
220  }
221}
222
223proc ::tolserver::on_bad_request { chan idrq reason } {
224  log notice "bad request '$idrq' '$reason'"
225  send_to_client $chan "CRITICAL" $reason
226}
227
228###############################################################################
229#
230#  send_to_client -- send data to the client channel
231#
232#  Arguments:
233#
234#    chan - client socket channel who should be waiting for a server answer.
235#    TAG   - identification of the data
236#    data - data to be send to client.
237#
238###############################################################################
239proc ::tolserver::send_to_client { chan TAG {data ""} } {
240
241  log "debug" "enter : send_to_client $chan $TAG '$data'"
242
243  if {[catch {
244    puts $chan [string trimright "$TAG [string length $data] $data"]
245    flush $chan
246  } msg]} {
247    log emergency $msg
248  }
249
250  log "debug" "leave : send_to_client"
251}
252
253###############################################################################
254#
255#  on_ping -- check is the server is responding
256#
257#  Arguments:
258#
259#    chan - client socket channel who should be waiting for a server answer.
260#
261###############################################################################
262proc ::tolserver::on_ping { chan idrq } {
263
264  log "debug" "enter : on_ping $chan $idrq"
265
266  send_to_client $chan ACKPING $idrq
267
268  log "debug" "leave : on_ping $chan $idrq"
269}
270
271###############################################################################
272#
273#  on_toleval -- Invoked in response to a TOLEVAL request
274#
275#  This proc evaluate a tol expression requested from client connected
276#  at the oder end of channel $chan. The tol expression is contained
277#  in argument cmd. First the expression is inserted into a queue and
278#  a requestd ID is returned to the client. When idle the server will
279#  process the tol expressions waiting for being evaluated. After and
280#  expression evaluation is done a TFINISH is sent to the
281#  corresponding client.
282#
283#  Arguments:
284#
285#    chan -- client channel from where the RUN request was sent.
286#    idreq -- request ID, must be send back when evaluation is finished
287#    texpr  -- the tol expression to be executed.
288#
289###############################################################################
290proc ::tolserver::on_toleval { chan idrq cmd }  {
291
292  log "debug" "enter, on_toleval $chan $idrq $cmd"
293
294  log "notice" "TOLEVAL $cmd"
295  set idtask [insert_expr $chan $idrq $cmd]
296  send_to_client $chan "TSTART" "$idrq $idtask"
297  after idle ::tolserver::dispatch_to_slave
298 
299  log "debug" "leave, on_toleval"
300}
301
302proc ::tolserver::on_reconnect { chan idrq idtask } {
303  # NO ESTA IMPLEMENTADO
304}
305
306###############################################################################
307#
308#  on_shutdown -- Handler for shutdown request
309#
310#  This proc perfom will shutdown this tol server. Before dying it
311#  will send an abort signal to all clients waiting for a request to
312#  finish.
313#
314#  Arguments:
315#
316#    chan - client socket channel from where the KILL request was sent.
317#    PID  - process ID to kill.
318#
319#  Return:
320#
321###############################################################################
322proc ::tolserver::on_shutdown { chan }  {
323
324  upvar \#0 ::tolserver::${chan}_data channel_data
325
326  log "debug" "enter, on_shutdown $chan"
327
328  log information "shutdown from $chan [get_client_id $chan]"
329  broadcast $chan "ACKSD"
330
331  after idle shutdown
332
333  log "debug" "leave, on_shutdown $chan"
334}
335
336###########################################################################
337#
338#
339###########################################################################
340proc ::tolserver::on_cancel { chan idrq } {
341
342  variable data
343
344  log notice "TCANCEL [get_client_id $chan] $idrq"
345
346  lappend data(queue,cancel) $idrq
347}
348
349###############################################################################
350#
351#  on_eof_channel -- close a client channel normally after an eof received.
352#
353#  This proc perform the following action:
354#
355#    - File event associated to the channel is cleared.
356#    - The client channel is closed.
357#    - The event is logged.
358#    - The channel specific data are cleared.
359#
360###############################################################################
361proc ::tolserver::on_eof_channel { chan } {
362  upvar \#0 ::tolserver::${chan}_data channel_data
363
364  log "debug" "enter, on_eof_channel $chan"
365  # Clear the file event.
366  #
367  fileevent $chan readable {}
368
369  # Close the client channel.
370  #
371  catch {close $chan}
372
373  # Log the situation.
374  #
375  log "notice" "$chan attached to [get_client_id $chan] has being closed"
376
377  # Clear the channel specific data.
378  #
379  array unset channel_data
380}
381
382###############################################################################
383#
384#  on_client_connected -- Invoked whenever the a client a connected.
385#
386#  Arguments:
387#
388#    chan - client channel just connected
389#    host - host address from where the channel was connected
390#    port - port from where the channel was connected in the host
391#
392###############################################################################
393proc ::tolserver::on_client_connected { chan host port } {
394  # Connection has been accepted. Log connection from client
395  #
396  log "debug" "enter, on_client_connected $chan $host $port"
397
398  log "notice" "Connection accepted from client ${host}:${port} through channel ${chan}"
399
400  # Create a variable for storing client channel specific data.
401  #
402  upvar \#0 ::tolserver::${chan}_data channel_data
403
404  set channel_data(host) $host
405  set channel_data(port) $port
406
407  log "debug" "leave, on_client_connected"
408}
409
410###############################################################################
411#
412#  open_socket -- Open the server socket
413#
414#  Arguments:
415#
416#    port - the port where this server will attend request from clients.
417#
418###############################################################################
419proc ::tolserver::open_socket { port } {
420  variable data
421
422  set data(socket) [socket -server [namespace code register_client] $port]
423}
424
425###############################################################################
426#
427#  close_socket -- Closes the server socket
428#
429###############################################################################
430proc ::tolserver::close_socket { } {
431
432  variable data
433
434  log "notice" "closing server socket ...."
435  catch {close $data(socket)}
436  set data(socket) ""
437}
438
439###############################################################################
440#
441#  start -- Start the server
442#
443#  Arguments:
444#
445#    port - the port where this server will attend request from clients.
446#
447###############################################################################
448proc ::tolserver::start { args } {
449  global shared_tasks
450
451  array set options {
452    -port 6669
453    -logfile ""
454    -compile ""
455  }
456  array set options $args
457
458  ::tlogger::init tolserver $options(-logfile)
459  ::tlogger::level tolserver "debug"
460
461  if {[catch {open_socket $options(-port)} msgerr]} {
462    log emergency "::tolserver::start '$msgerr'"
463    exit
464  } else {
465    log "notice" "started"
466  }
467 
468  server_attach_data
469
470  log "debug" "shared_tasks(init)=$options(-compile)"
471  set shared_tasks(init) $options(-compile) 
472  set shared_tasks(server_init) 1
473}
474
475proc ::tolserver::server_attach_data {} {
476  package require tequila
477  global shared_tasks
478  global shared_state
479
480  tequila::open localhost 20458
481  tequila::attach shared_tasks
482  tequila::attach shared_state
483 
484  # this trace is just for debbuging, it's not necesary actually
485  trace add var shared_tasks write tolserver::server_listen_tasks
486 
487  # this trace is just for debbuging, it's not necesary actually
488  trace add var shared_state write tolserver::server_listen_state
489}   
490
491proc tolserver::server_listen_tasks {name index op} {
492  global shared_tasks
493
494  log "debug" "server_listen_tasks:index=$index"
495  log "debug" "[array get shared_tasks]"
496}
497
498proc tolserver::server_listen_state {name index op} {
499  global shared_state
500
501  log "debug" "server_listen_state:index=$index"
502  log "debug" "[array get shared_state]"
503}
504
505###############################################################################
506#
507#  shutdown -- Perform shutdown task for the server.
508#
509###############################################################################
510proc ::tolserver::shutdown { } {
511  log "notice" "shutting down immediately"
512  close_socket
513  exit
514}
515
516###############################################################################
517#
518#  bgerror -- handle unxepected error detected in background
519#
520###############################################################################
521proc _bgerror {msg} {
522  # Handle unexpected errors here.
523  ::tolserver::log "emergency" "from _bgerror --> $msg"
524}
525
526proc ::tolserver::broadcast {chan what} {
527  set client [get_client_id $chan]
528  log "notice" "broadcasting $what requested from $client"
529  log "warn" "TO BE IMPLEMENTED"
530  # TO BE IMPLEMENTED
531}
532
533###########################################################################
534#
535#
536###########################################################################
537proc ::tolserver::insert_expr { chan idrq tol_expr } {
538  upvar \#0 ::tolserver::${chan}_data channel_data
539  variable data
540 
541  log "debug" "enter, insert_expr $chan $idrq $tol_expr"
542
543  incr data(queue,id)
544  set client [get_client_id $chan]
545  set idtask "${client}:$data(queue,id)"
546  push_queue_tasks [list $idtask $idrq $chan $tol_expr]
547
548  log "debug" "leave, insert_expr, $idtask"
549  set idtask
550}
551
552proc ::tolserver::push_queue_tasks {item} {
553  variable data
554
555  log "debug" "push_queue_tasks:item=$item"
556  lappend data(queue,tasks) $item
557}
558
559proc ::tolserver::dispatch_to_slave {} {
560  global shared_state
561
562  log "debug" "enter dispatch_to_slave"
563  if {$shared_state(slave) ne "ready"} {
564    log "debug" "dispatch_to_slave: vwait shared_state(slave)"
565    vwait shared_state(slave)
566  }
567  send_tasks_to_slave
568  log "debug" "leave dispatch_to_slave"
569}
570
571proc ::tolserver::send_tasks_to_slave {} {
572  #global shared_data
573
574  log "debug" "enter send_tasks_to_slave"
575  set t_list [get_queue_tasks]
576  if {[llength $t_list] != 0} {
577    set_queue_shared $t_list
578  }
579  #if {$shared_data(ack) == 0} {
580    #log "debug" "send_tasks_to_slave: vwait shared_data(ack)"
581    #vwait shared_data(ack)
582  #}
583  log "debug" "leave send_tasks_to_slave"
584}
585
586proc ::tolserver::get_queue_tasks {} {
587  variable data
588
589  set t_list $data(queue,tasks)
590  set data(queue,tasks) [list]
591  log "debug" "get_queue_tasks:t_list=$t_list"
592  return $t_list
593}
594
595proc ::tolserver::set_queue_shared {t_list} {
596  global shared_tasks
597 
598  log "debug" "set_queue_shared:t_list=$t_list"
599  set shared_tasks(request) $t_list
600}
Note: See TracBrowser for help on using the repository browser.