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

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

refs#873: Improve TolServer process structure
Use of another shared array in order to inform client that a task has being finished

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

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