Example files in this directory are meant to illustrate features of pycos
framework. They are not necessarily efficient versions, nor very useful. The
descriptions below are sorted on the names of files, so some examples at the
beginning use advanced features, whereas later files may be easier to follow.

In the examples (as in Pycos implementation), 'raise StopIteration(value)' is used to exit task
functions (generators). In Python 3.8+, 'StopIteration' has been removed and instead 'return' must
be used. Pip installer for pycos translates 'raise StopIteration' to 'return' appropriately when
installing with Python 3.8+. When programming for Python 3.8+ only, use 'return'.

In some cases, 'encode' method is called on strings so same code works with Python 2 (where
'encode' returns string itself) and Python 3 (where 'encode' returns bytes as needed). When
programming with Python 2 exclusively, there is no need to call 'encode'.

dispy project (http://dispy.sourceforge.net) uses pycos to implement Python
framework for distributed and parallel computing.

* channel.py broadcasts messages in local tasks.

* chat_chan_client.py and chat_chan_server.py use broadcasting over a channel to
  send messages to all participants to implement a simple chat (message)
  service.  To use this and other 'chat' examples below, run the server, and
  multiple clients (either on same machine or other machines in local
  network). Messages typed in one client show up at other clients.

* chat_client.py and chat_server.py are alternative and simpler versions of
  chat_chan_client.py and chat_chan_server.py.

* chat_sock_client.py and chat_sock_server.py use asynchronous network
  programming, tasks and message passing to implement a chat (message) server
  that is used by clients to broadcast messages.

* client.py and server.py show message passing between remote client and server
  tasks.

* client_server.py shows message passing between local client and server
  tasks. The remote version and local version are similar, except that remote
  versions register/locate tasks.

* dispycos_client1.py is a variation of dispycos_client1.py. In this example,
  http server is used to monitor cluster, nodes, remote tasks.

* dispycos_client2.py illustrates how to use dispycos to distribute computations
  to remote servers to run them as tasks on those servers and get results back
  to client.

* dispycos_client3.py shows how to exchange messages with objects (instances of
  class) between client and remote tasks.

* dispycos_client4.py sends files at the client to remote process to execute
  computations that process those files and the remote process in turn sends the
  results in files back to the client.

* dispycos_client5.py runs an external program (dispycos_client5_proc.py) at
  remote servers. The program reads from standard input and writes to standard
  output. Asynchronous pipes and message passing are used to send input from
  client to this program executing on remote servers, and get the output back to
  client.

* dispycos_client7.py is an alternate implementation of dispycos_client1.py; it
  uses messages from dispycos scheduler to schedule remote tasks and get
  results.

* dispycos_client8.py demonstrates that long-runnning computations without
  'yield' often can be executed. In this case, 'time.sleep' is used to simulate
  computation. Note that 'time.sleep' blocks entire pycos framework, so no other
  tasks can execute until next 'yield'. With version 4.1 (and above), I/O
  processing, message passing, sending heartbeat messages to scheduler etc. are
  handled by a separate (called "reactive") pycos scheduler that is not affected
  by user's tasks. So messages sent by client are received and queued by
  reactive scheduler.

* dispycos_client9_node.py uses status messages from dispycos scheduler to
  distribute data files to nodes and run node specific setup task to load the
  data in memory. This data is then processed in computations to illustrate
  in-memory processing. This example doesn't work with Windows (due to lack of
  'fork' in Windows), so nodes running Windows are filtered out using
  DispycosNodeAllocate.

* dispycos_client9_server.py is similar to dispycos_client9_node.py above,
  except that instead of initializing (memory in) nodes, each server in each
  node is initialized by distributing one file per server (note that one node
  may run as many servers as there are processors on that node), which is then
  read in memory on that server for in-memory processing at server level.

* dispycos_client10.py shows how to close and restart servers to start computations with a
  pristine (global) state.

* dispycos_cloud_computing.py shows configuration for cloud computing using EC2
  nodes as servers and client on local computer.

* dispycos_httpd1.py shows how to use httpd module to provide HTTP interface to
  monitor dispycos cluster.

* dispycos_httpd2.py is a variant of dispycos_httpd1.py to use 'status_task' to
  process messages from dispycos scheduler (in this case just to print when a
  remote task is finished) while also using httpd module (which chains messages
  from dispycos scheduler to client's 'status_proc').

* dispycos_live_analytics.py shows how to implement live/real-time analytics and send the results
  back to client. It also uses streaming of data to remote tasks for efficient communication.

* dispycos_mas.py is a simple example of multi-agent system where computations (agents) discover
  each other, communicate with each other etc..

* dispycos_remote_scheduler.py uses remote (shared) scheduler (instead of
  starting scheduler itself). It also uses customized (subclassed)
  DispycosNodeAllocate to allocate CPUs at nodes.

* dispycos_ssh_ec2.py shows how to use ssh port forwarding to work with Amazon
  EC2 cloud computing. In this example client runs locally and dispycosnode runs
  on remote Amazon EC2 cloud infrastructure.

* hotswap.py and hotswap_funcs.py illustrate how a running task function can be
  swapped with a new function. The currently running function checks/validates
  the function being replaced, any unprocessed messages in the task are
  processed with new functionality.

* pico_client_task.py and pico_service_task.py are variation of RPS version above
  using (remote) tasks and message passing.

* pipe_csum.py uses asynchronous pipes to write data to and read data from a
  system program (that computes checksum of data).

* pipe_grep.py uses chained pipes with asynchronous read and write interface to
  count number of lines matching a pattern.

* remote_channel_client.py and remote_channel_server.py use broadcasting
  channels to exchange messages among a sender and local/remote recipients.

* remote_task_client.py and remote_task_server.py exchange messages with
  one-to-one message passing to exchange messages between two remote tasks.

* rps_log_watch_client.py and rps_log_watch_server.py use RPS to implement
  log monitoring where each node running log_monitor_server sends filtered log messages
  to client. This is useful to observe many nodes for potential issues
  (e.g., authentication failures).

* rps_monitor_client.py and rps_monitor_server.py illustrate another approach to
  execute remote tasks: The server registers a function and client requests to
  execute task with that function. Compare this to dispycos_client.py where the
  client sends the computation itself to the remote server, so the client can
  execute arbitrary functions, whereas with RPS only registered functions can be
  executed by clients.

* rps_node_client.py and rps_node_server.py use RPS feature so client can run
  a script (node_update_script.py in this example) on the server. Authentication is
  used to prevent unauthorized use of this feature.

* rps_pico_client.py and rps_pico_service.py use RPS feature and message passing
  to show how a tiny (pico size) service can be implemented.

* sock_client.py and sock_server.py use asynchronous network programmming to
  communicate.

* socket_afile.py creates a server and a client connected with a socket, which
  is then converted to asynchronous file. The server and client exchange data
  with asynchronous file interface. This example doesn't work in Windows, as
  sockets in Windows don't have underlying file.

* tasks.py creates a number of tasks that each suspend execution for a brief
  period. The number of tasks created can be increased to thousands or tens of
  thousands to show pycos can scale well.

* udp.py creates client server tasks that communicate using asynchronous UDP
  sockets.

* webserver.py is an impelementation of "Ping Pong" benchmark server described
  at http://nichol.as/asynchronous-servers-in-python.
