The Perl Toolchain Summit needs more sponsors. If your company depends on Perl, please support this very important event.

NAME

BgTask Supervisor

SYNOPSIS

  # supervisor is created behind the scenes
  $taskPool->spawn(cmd => 'while sleep 1; do date; done');
  
  # to write your own supervisor
  use strict;
  use warnings;
  use RapidApp::BgTask::Supervisor;
  RapidApp::BgTask::Supervisor->script_main

DESCRIPTION

This is the main object implementing a job supervisor for the BgTask system.

Supervisor has a delicate startup sequence, whereby the parent process tries to wait until it is fairly sure that the supervisor has started successfully before fully detaching. To make life easy, use "script_main" to perform this sequence.

Supervisor uses the RMI feature of BgTask::MsgPipe to let clients call methods on the supervisor object. This is very much a security concern, so use appropriate filesystem permissions to keep untrusted clients from connecting to the socket.

Supervisor has a number of methods intended to be called by clients. It also has lots of other methods which probably shouldn't be called by the client. It would perhaps have been a good idea to separate out the public methods into a new package, and proxy them back to the Supervisor object.

I didn't do this yet, so here is a list: terminate getInfo applyMeta kill restart getStreamInfo readStream writeStream closeStream

You will find these nicely wrapped up by the BgTask::Task class. Use that when possible.

HOW TO DEBUG

Debugging can be difficult, since this runs as a background job disconnected from the terminal. If you pass the parameter "behavior => 'foreground'", and encode your parameters into Storable format on stdin, you can run it in the foreground:

  perl -e 'use Storable "freeze"; print freeze { exec => [ "/usr/bin/cat" ], meta => { name => "Hello World" }, behavior => "foreground" };' > foo
  
  perl -e 'use RapidApp::BgTask::Supervisor; RapidApp::BgTask::Supervisor->script_main' < foo

If you aren't sure what your parameters need to be, set the DEBUG_BGTASK environment variable and then call BgTask::TaskPool->spawn() with appropriate official parameters, and it will write out /tmp/bgtask_serialized_params.sto for you. Then use that.

You can also enable DEBUG_SUPERVISOR for lots of diagnostic info.

take_lockfile( $fname, \$result_existing_pid )

This function opens the named lockfile, marks it close-on-exec, and then acquires a write-lock on it.

It also writes its pid to this file in case others are interested.

The file is not closed for the duration of the script, nor is the file ever unlinked. This makes a very secure way to ensure only one copy of a service is running.

If the file cannot be locked, this returns false, and places the pid of the process holding the lock into the optional $$result_existing_pid

$supervisor->terminate [PUBLIC]

Terminates the supervisor, killing the child proc and losing all in/out data

$supervisor->getInfo [PUBLIC]

Returns a hash of information about the task.

$supervisor->applyMeta( \%newHashData -or- key => $value, ... )

Merges new hash data with the existing meta hash.

$supervisor->restart() [PUBLIC]

If the child process is not running, start it again using the same paramteters as the first time.

$supervisor->getStreamInfo( $streamId ) [PUBLIC]

Like getInfo, but only data for one stream.

StreamId should be a numeric file descriptor number of the child process.

$supervisor->readStream( streamId => $streamId, ofs => $offset, count => $byteCount, peek => $bool, discard => $bool ) [PUBLIC]

$streamId is a numeric file descriptor number of the child. Call getInfo for a list of streams. (which are always [0, 1, 2] for now) And in fact, only 1 and 2 are ever readable.

$offset is a byte offset from the first byte read form the stream. Not all bytes might be available. Call getStreamInfo to find the minimum $offset allowed. However, setting $offset to too small of a number will still return a result with "ofs" set to the earliest offset currently available. (so an $offset of 0 will always return a packet of the earliest data)

$count is the number of bytes to retrieve. If fewer are available, fewer will be returned.

If 'peek' is set, no data is removed from the buffer. If ppek is not set, all read bytes will be removed and no longer available.

If 'discard' is set, the data will be removed from the buffer, but not returned. Use this if you want to remove data from the buffer but don't want to waste the time to have it sent to you.

$supervisor->writeStream( streamId => $streamId, data => $data ) [PUBLIC]

Write a scalar of bytes to the specified stream.

StreamId currently must be 0 (STDIN is the only writeable stream)

$supervisor->closeStream( streamId => $streamId, immediate => $bool ) [PUBLIC]

This closes a stream. If immediate is set, it will close the stream before making sure that previous writes are complete. Otherwise, it waits for all data to be consumed by the child, then closes the stream.

Streams will always be closed immediately if they have not been written to.

$supervisor->kill( $sigName ) [PUBLIC]

Send the specified signal to the child process.

$supervisor->distributeEvent( \%event )

Supervisor sends out event messages to all clients about activity on streams or the child exiting.

This method sends those events to all clients.

This also helps to determine when a client has closed its handle. The onErr of the MsgPipe will clean up the socket and remove the client form the list. (this is why we make a copy of the client list first)