Class ::xotcl::THREAD

::xotcl::THREAD[i] create ... \
           [ -exithandler (default "ns_log notice "EXITHANDLER of slave thread SELF [pid]"") ] \
           [ -lightweight (default "0") ] \
           [ -persistent (default "0") ]

Defined in

Class Relations

  • class: ::xotcl::Class[i]
  • superclass: ::xotcl::Object[i]
::xotcl::Class create ::xotcl::THREAD \
     -superclass ::xotcl::Object

Methods (to be applied on the object)

  • recreate (scripted, private)

     xotcl::THREAD[i] recreate obj args [ args... ]

    this method catches recreation of THREADs in worker threads it reinitializes the thread according to the new definition.

    Parameters:
    obj (required)
    args (required)

    Testcases:
    No testcase defined.
    :log "recreating [self$obj, tid [$obj exists tid]"
    if {![string match "::*" $obj]} { set obj ::$obj }
    $obj set recreate 1
    next
    $obj init [lindex $args 0]
    if {[nsv_exists [self$obj] && [$obj exists initcmd]} {
      set tid [nsv_get [self$obj]
      ::thread::send $tid [$obj set initcmd]
      $obj set tid $tid
      :log "+++ content of thread $obj ($tid) redefined"
    }

Methods (to be applied on instances)

  • check_blueprint (scripted)

    if {![[self class] exists __blueprint_checked]} {
      if {[string first ::xotcl::THREAD [ns_ictl get]] == -1} {
        _ns_savenamespaces
      }
      [self class] set __blueprint_checked 1
    }
  • destroy (scripted)

    :log "destroy called"
    if {!${:persistent} &&
        [nsv_exists [self class] [self]]} {
      set tid [nsv_get [self class] [self]]
      set refcount [::thread::release $tid]
      :log "destroying thread object tid=$tid cnt=$refcount"
      if {$refcount == 0} {
        :log "thread terminated"
        nsv_unset [self class] [self]
        ns_mutex destroy ${:mutex}
        ns_log notice "mutex ${:mutex} destroyed"
      }
    }
    next
  • do (scripted)

    if {![nsv_exists [self class] [self]]} {
      # lazy creation of a new slave thread
    
      ad_mutex_eval ${:mutex} {
        #:check_blueprint
        #:log "after lock"
        if {![nsv_exists [self class] [self]]} {
          if {${:lightweight}} {
            :log "CREATE lightweight thread"
            set tid [::thread::create -thin]
          } else {
            set tid [::thread::create]
          }
          nsv_set [self class] [self$tid
          if {${:persistent}} {
            :log "--created new persistent [self class] as $tid pid=[pid]"
          } else {
            :log "--created new [self class] as $tid pid=[pid]"
          }
          #:log "--THREAD DO send [self] epoch = [ns_ictl epoch]"
          if {${:lightweight}} {
          } elseif {![ns_ictl epoch]} {
            #ns_log notice "--THREAD send [self] no epoch"
            # We are during initialization. For some unknown reasons, XOTcl
            # is not available in newly created threads, so we have to care
            # for full initialization, including xotcl blueprint.
            _ns_savenamespaces
            set initcmd [ns_ictl get]
          }
          append initcmd ${:initcmd}
          #ns_log notice "INIT $initcmd"
          ::thread::send $tid $initcmd
    
          #
          # There is a potential race condition during startup on a very
          # slow/busy system, where the throttle thread can receive
          # commands, although it is not full initialized. One approach
          # would be to move the nsv setting of the pid here, where the
          # thread is fully initialized, .... but unfortunately, this
          # leads to problems as well. This needs deeper investing,
          # ... but is not very important, since it is very hard to
          # reconstruct the problem case.
          #
          #nsv_set [self class] [self] $tid
        } else {
          set tid [nsv_get [self class] [self]]
        }
      }
    } else {
      #
      # Target thread is already up and running.
      #
      set tid [nsv_get [self class] [self]]
    }
    if {![info exists :tid]} {
      #
      # This is the first call.
      #
      if {!${:persistent} && ![info exists :recreate]} {
        #
        # For a shared thread, we do ref-counting through preserve.
        #
        set tid [nsv_get [self class] [self]]
        :log "THREAD::PRESERVE must preserve for sharing request-thread [pid] tid $tid"
        ::thread::preserve $tid
      }
      set :tid $tid
    }
    if {[ns_info shutdownpending]} {
      :log "thread send operation ignored due to pending shutdown: $args"
    } elseif {$async} {
      return [thread::send -async $tid $args]
    } else {
      return [thread::send $tid $args]
    }
  • exithandler (setter)

  • get_tid (scripted)

    if {[nsv_exists [self class] [self]]} {
      # the thread was already started
      return [nsv_get [self class] [self]]
    }
    # start a small command in the thread
    :do info exists x
    # now we have the thread and can return the tid
    return ${:tid}
  • init (scripted)

    if {$cmd eq "-noinit"} {return}
    #ns_log notice "+++ THREAD cmd='$cmd', epoch=[ns_ictl epoch]"
    if {![ns_ictl epoch]} {
      #ns_log notice "--THREAD init [self] no epoch"
    
      # We are during initialization. For some unknown reasons, XOTcl
      # is not available in newly created threads, so we have to care for it.
      # We need only a partial initialization, to allow the exit handler
      # to be defined.
      set :initcmd {
        package req XOTcl
        namespace import -force ::xotcl::*
      }
    }
    append :initcmd {
      ns_thread name SELF
    }
    append :initcmd [subst {
      ::xotcl::Object setExitHandler [list [:exithandler]]
    }]
    regsub -all SELF ${:initcmd} [self] :initcmd
    append :initcmd \n [list set ::xotcl::currentScript [info script]] \n [list set ::xotcl::currentThread [self]] \n $cmd
    set :mutex [ns_mutex create ns_mutex[self]]
    ns_log notice "mutex ${:mutex} created"
    next
  • lightweight (setter)

  • persistent (setter)