AOLserver

# Thread, Cache, and Scheduling Functions

$Header: /cvsroot/aolserver/aolserver.com/docs/devel/tcl/api/thread.html,v 1.2 2002/03/19 20:30:11 kriston Exp $

## ns_cond

Manage events

### Syntax

ns_cond broadcast eventid
ns_cond create
ns_cond destroy eventid
ns_cond set eventid
ns_cond wait eventid lockid ?timeout?

### Description

Note that this function is the same as the ns_event function, except that ns_cond has a more efficient underlying implementation.

ns_cond broadcast wakes up all threads waiting on the specified event. The eventid argument is the event ID returned by ns_cond create when the event was created.

ns_cond create initializes an event and returns an event ID for the new event.

ns_cond destroy frees the resources associated with the specified event. The eventid argument is the event ID returned by ns_cond create when the event was created.

ns_cond set wakes up the specified event. The eventid argument is the event ID returned by ns_cond create when the event was created.

ns_cond wait waits for the specified event for a specified time. The eventid argument is the event ID returned by ns_cond create when the event was created. The lockid argument is the id of a mutex lock. The timeout argument is the time to wait in seconds. The return value is 0 (zero) if the event timed out and 1(one) if the event was woken up.

## ns_critsec

Manage critical sections

### Syntax

ns_critsec create
ns_critsec destroy csid
ns_critsec enter csid
ns_critsec leave csid

### Description

ns_critsec create creates a critical section and returns a critical section ID.

ns_critsec destroy destroys the specified critical section. The csid argument is the critical section ID returned by ns_critsec create when the critical section was created.

ns_critsec enter enters the specified critical section. The csid argument is the critical section ID returned by ns_critsec create when the critical section was created.

ns_critsec leave leaves the specified critical section. The csid argument is the critical section ID returned by ns_critsec create when the critical section was created.

## ns_event

Manage events

### Syntax

ns_event broadcast eventid
ns_event create
ns_event destroy eventid
ns_event set eventid
ns_event wait eventid lockid ?timeout?

### Description

ns_event broadcast wakes up all threads waiting on the specified event. The eventid argument is the event ID returned by ns_event create when the event was created.

ns_event create initializes an event and returns an event ID for the new event.

ns_event destroy frees the resources associated with the specified event. The eventid argument is the event ID returned by ns_event create when the event was created.

ns_event set wakes up the specified event. The eventid argument is the event ID returned by ns_event create when the event was created.

ns_event wait waits for the specified event for a specified time. The eventid argument is the event ID returned by ns_event create when the event was created. The lockid argument is the id of a mutex lock. The timeout argument is the time to wait in seconds. The return value is 0 (zero) if the event timed out and 1(one) if the event was woken up.

## ns_mutex

Manage mutexes

### Syntax

ns_mutex create
ns_mutex destroy mutexid
ns_mutex lock mutexid
ns_mutex unlock mutexid

### Description

ns_mutex create initializes a mutual exclusion lock and returns an ID for it.

ns_mutex destroy frees the resources associated with the specified mutual exclusion lock. The mutexid argument is the mutex ID returned by ns_mutex create when the mutex was created.

ns_mutex lock acquires the specified mutual exclusion lock. The mutexid argument is the mutex ID returned by ns_mutex create when the mutex was created.

ns_mutex unlock unlocks the specified mutual exclusion lock. The mutexid argument is the mutex ID returned by ns_mutex create when the mutex was created.

### Example

At startup (for example, in your init.tcl procedure), open a shared file and create a lock for it. Note that this is very unscalable due to the fact that many threads can be waiting around for the one lock.

    ns_share Shared
    set Shared(file) [open myfile.data]
    set Shared(lock) [ns_mutex create]
    detach $Shared(file)

Later (for example, in a request procedure), access the data file:

    ns_share Shared
    ns_mutex lock $Shared(lock)
    catch {
        ... access $Shared(file) ...
    }
    ns_mutex unlock $Shared(lock)

Note: The "catch" is important so the lock isn't held if Tcl unwinds due to an error accessing the file.

At shutdown (for example, in your shutdown procedure registered with ns_atshutdown), close the file and destroy the lock:

    ns_share Shared
    close $Shared(file)
    ns_mutex destroy $Shared(lock)

Try to avoid ns_share whenever possible. The lock contention will kill your server's performance. Investigate using the nsv commands instead to share data among multiple interpreters and multiple threads.

## ns_rwlock

Create, destroy, and manipulate read/write locks

### Syntax

ns_rwlock create
ns_rwlock destroy rwlockid
ns_rwlock readlock rwlockid
ns_rwlock readunlock rwlockid
ns_rwlock writelock rwlockid
ns_rwlock writeunlock rwlockid

### Description

ns_rwlock create initializes a read/write lock and returns an ID for it.

ns_rwlock destroy frees the resources associated with the specified read/write lock. The rwlockid argument is the read/write lock ID returned by ns_rwlock create when the lock was created.

ns_rwlock readlock acquires a read lock. Any number of read locks can be pending. If there's a write lock active, the read lock acquisition blocks until the write lock is released.

ns_rwlock readunlock releases a read lock.

ns_rwlock writelock acquires a write lock. Only one write lock can be in effect. If there are pending read locks active, the write lock acquisition blocks until all of the read locks drain. If a subsequent read lock acquisition attempt is made, the write lock has priority.

ns_rwlock writeunlock releases a write lock

About Read/Write Locks

Read/write locks are a serialization mechanism for using data structures where multiple reads can happen simultaneously, but where writes must happen singly. For example, suppose you have a hash table that is heavily used but doesn't change very often. You'd like to have multiple threads be able to read from the table without blocking on each other, but when you need to update the table, you can do so safely without having to worry about other threads reading incorrect data.

The principal feature of read/write locks is the mechanism of which locks have priority and which locks must wait. Any number of read locks can be pending. If there's a write lock active, the read lock acquisition blocks until the write lock is released. Also, only one write lock can be in effect. If there are pending read locks active, the write lock acquisition blocks until all of the read locks drain. If a subsequent read lock acquisition attempt is made while a write lock is waiting to acquire, the write lock has priority.

## ns_sema

Manage semaphores

### Syntax

ns_sema create ?count?
ns_sema destroy semaid
ns_sema release semaid ?count?
ns_sema wait semaid

### Description

ns_sema create initializes a semaphore and returns an ID for it. If the count argument is specified, the semaphore is initialized with that count. The default for count is 0.

ns_sema destroy frees the resources associated with the specified semaphore. The semaid argument is the ID returned by ns_sema create when the semaphore was created.

ns_sema release increments the count of the specified semaphore. By default, the semaphore is incremented 1 (one) time. If the count argument is specified, the semaphore is incremented count times. The semaid argument is the ID returned by ns_sema create when the semaphore was created.

ns_sema wait waits for the count of the specified semaphore to be greater than 0 (zero). If it is greater than 0, the count is decremented and processing continues. If it is not greater than 0, it is blocked until this is possible. The semaid argument is the ID returned by ns_sema create when the semaphore was created.

## ns_thread

Manage threads

### Syntax

ns_thread begin script
ns_thread begindetached script
ns_thread get
ns_thread getid
ns_thread wait tid
ns_thread yield

### Description

ns_thread begin begins a new thread which evaluates the specified script and then exits. It returns a thread ID that must eventually be passed to ns_thread wait. (Failing to call ns_thread wait will eventually result in no new threads being created.)

ns_thread begindetached begins a detached thread that doesn't have to be (and can't be) waited for.

ns_thread get gets the thread ID of the current thread. The result is a thread ID that can be passed to ns_thread wait and may look something like "tid532".

ns_thread getid gets the thread integer number for the current thread. The result is a small integer used for identifying threads is a human-readable way, such as "1" or "1120", for example.

ns_thread wait waits for the specified thread to exit. The tid argument is a thread ID returned by ns_thread begin or ns_thread get.

ns_thread yield causes the current thread to yield.

### Example

This example is similar to the example under the ns_sockselect function of connecting to the 10 servers and waiting to service them with the ns_sockselect command. In this case, though, each connection gets it's own thread.

    # This is the procedure which is evaluated for each thread and
    # handles a single connection to host number $i

    proc getpage {i} {
        global pages
        # new thread will start here - first connect to host
        set host [format "www%2d.foo.com" $i]
        set fds [ns_sockopen $host 80
        set r [lindex $fds 0]
        set w [lindex $fds 1]
        # next, send request
        puts $w "GET /index.htm HTTP/1.0\r\n\r"
        flush $w
        # then read page
        set pages($i) [read $r]
        # and close sockets
        close $w
        close $r
        # thread goes away here and other threads waiting
        # on ns_thread wait will wakeup
    }

    # Here's the loop which creates the threads which run getpage.
    for {set i 1} {$i < 9} {incr i} {
        set tids($i) [ns_thread begin "getpage $i"]
    }

    # wait for the threads to exit and then process the pages
    for {set i 1} {$i < 9} {incr i} {
        ns_thread wait $tids($i)
        # output page
        ... process the page in $pages($i) put there by other thread ...
    }

Note that the code here is much simpler to follow than the ns_sockselect example; that's the benefit of multithreaded programming. However, it uses more resources as threads need to be created and initialized. This can be a problem if you plan to create many threads.

## ns_cache_flush

Remove cache entry.

### Syntax

ns_cache_flush cache key

### Description

This function removes the cache entry specified by key from the cache specified by cache.

## ns_cache_names

Return cache names.

### Syntax

ns_cache_names

### Description

This function returns a Tcl list of cache names.

## ns_cache_size

Return size of cache.

### Syntax

ns_cache_size cache

### Description

This function returns the size of the specified cache.

## ns_cache_stats

Return cache statistics.

### Syntax

ns_cache_stats cache ?arrayvar?

### Description

If no arrayvar is specified, this function returns cache statistics in the following format:

        "entries: #   flushed: #   hits: #   misses: #   hitrate: #"

If an arrayvar is specified, this fills the array with the elements: entries, flushed, hits, misses, and hitrates. The entries are populated with their corresponding values.

See the nstelemetry.adp utility for an example of how ns_cache_stats is used.

## ns_atclose

Register a script to be executed when the current connection closes

### Syntax

ns_atclose {script | procname ?args?}

### Description

ns_atclose adds the specified script or procedure (procname) to a list of scripts that are invoked when the current connection ends. The scripts are invoked in the reverse order in which they were registered. The scripts still get run if the connection closes early, or if there are Tcl errors following the script registration. If ns_atclose is invoked within a scheduled procedure, the atclose handler is invoked at the end of each scheduled execution of the procedure.

### Example

This example makes a temporary file that gets deleted when the connection ends:

    proc show_at_close { } {
        set tmpfile [ns_tmpnam]
        # open tmpfile, write stuff to it and close it
        ns_atclose "ns_unlink -nocomplain $tmpfile"
        return $tmpfile
    } ;# show_at_close

## ns_atexit

Register a script to be run at server shutdown

### Syntax

ns_atexit {script | procname ?args?}

### Description

ns_atexit registers a Tcl script or procedure to be run when the server shuts down. This function is the same as ns_atshutdown.

## ns_atshutdown

Register a script to be run at server shutdown

### Syntax

ns_atshutdown {script | procname ?args?}

### Description

ns_atshutdown registers a Tcl script or procedure to be run when the virtual server shuts down.

### Example

    proc theLastThing {} {
        global Shared
        close $Shared(file)
        ns_mutex destroy $Shared(lock)
    }

    ...
    ns_atshutdown theLastThing
    ...

## ns_atsignal

Register a script to be run in response to a SIGHUP signal

### Syntax

ns_atsignal {script | procname ?args?}

### Description

ns_atsignal registers a Tcl script or procedure to be run in response to a SIGHUP signal.

### Example

    proc dosomething blah {
        ns_log Notice "proc with arg '$blah'"
    }
    ns_atsignal dosomething $arg1

## ns_schedule_daily

Schedule a procedure to run once a day

### Syntax

ns_schedule_daily ?-thread? ?-once? hour minute {script | procname ?args?}

### Description

ns_schedule_daily runs the specified Tcl script or procedure (procname) once a day at the time specified by hour and minute. The hour can be from 0 to 23, and the minute can be from 0 to 59.

Specify -thread if you want a thread created to run the procedure. This will allow the scheduler to continue with other scheduled procedures. Specifying -thread is appropriate in situations where the script will not return immediately, such as when the script performs network activity.

Specify -once if you want the script to run only one time. The default is that the script will be re-scheduled after each time it is run.

ns_schedule_daily returns an id number for the scheduled procedure that is needed to stop the scheduled procedure with ns_unschedule_proc.

### Example

This example defines a script called rolllog that uses ns_accesslog to roll the access log to a file with an extension containing the current date. The ns_schedule_daily function is used to execute the rolllog script on a daily basis.

    # Script to roll and rcp log file to host "grinder"
    proc rolllog {} {
        set suffix [ns_strftime "%y-%m-%d"]
        set new [ns_accesslog file].$suffix
        ns_accesslog roll $new
        exec rcp $new grinder:/logs/[file tail $new]
    }
    # Schedule "rolllog" to run at 3:30 am each morning
    ns_schedule_daily -thread 3 30 rolllog

## ns_schedule_proc

Schedule a procedure to run at specified intervals

### Syntax

ns_schedule_proc ?-thread? ?-once? interval {script | procname ?args?}

### Description

ns_schedule_proc runs the specified Tcl script or procedure (procname) at an interval specified by interval. The interval is the number of seconds between runs of the script.

Specify -thread if you want a thread created to run the procedure. This will allow the scheduler to continue with other scheduled procedures. Specifying -thread is appropriate in situations where the script will not return immediately, such as when the script performs network activity.

Specify -once if you want the script to run only one time. The default is that the script will be re-scheduled after each time it is run.

ns_schedule_proc returns an id number for the scheduled procedure that is needed to stop the scheduled procedure with ns_unschedule_proc.

### Example

    proc dosomething blah {
        ns_log Notice "proc with arg '$blah'"
    }
    ns_schedule_proc 10 dosomething $arg1

## ns_schedule_weekly

Schedule a procedure to run once a week

### Syntax

ns_schedule_weekly ?-thread? ?-once? day hour minute {script | procname ?args?}

### Description

ns_schedule_weekly runs the specified Tcl script or procedure (procname) once a week on the day specified by day and the time specified by hour and minute. The day can be from 0 to 6, where 0 represents Sunday. The hour can be from 0 to 23, and the minute can be from 0 to 59.

Specify -thread if you want a thread created to run the procedure. This will allow the scheduler to continue with other scheduled procedures. Specifying -thread is appropriate in situations where the script will not return immediately, such as when the script performs network activity.

Specify -once if you want the script to run only one time. The default is that the script will be re-scheduled after each time it is run.

ns_schedule_weekly returns an id number for the scheduled procedure that is needed to stop the scheduled procedure with ns_unschedule_proc.

## ns_unschedule_proc

Stop a scheduled procedure.

### Syntax

ns_unschedule_proc id

### Description

ns_unschedule_proc stops a scheduled procedure from executing anymore. The scheduled procedure to be stopped is identified by its id, which was returned by the ns_schedule* function that was used to schedule the procedure.