The SILC Core Library

This library contains the implementation of various SILC protocol packet
payloads and other routines.  It also implements the SILC Packet Engine.


The SILC Packet Engine

The SILC Packet engine (silcpacket.[ch]) is the heart of sending and
receiving SILC packets.  The engine works in single thread but is thread
safe and has the notion of per-thread contexts for optimized processing.
The per-thread processing is actually per-scheduler, but in reality multiple
schedulers are run only when they are used in threads.

The packet engine has a lock (engine->lock) that protects various engine
wide data.  Currently this includes SILC Packet freelist, which could
perhaps later be in the per-thread SilcPacketEngineContext context.  The
engine also keeps list of all streams (SilcPacketStream) that has been
added to the engine.

The SilcPacketStream contains all stream related data, including encryption
and decryption keys, IDs, and outgoing buffer.  The outgoing buffer is
per-stream so that data can be sent in multiple pieces from the outbuffer
if writing would block.  Incoming buffer is not in stream context unless
it is necessary.  The incoming buffer is in the per-thread context which
actually has a list of incoming buffers.  If reading blocks for the given
stream, that incoming buffer is given to the SilcPacketStream context from
the list of buffers.  When data is again available for that stream it is
read into the buffer the stream already has.  Other stream will read to
the per-thread buffer, unless they would block also.  The stream also
has a lock (stream->lock) because application can modify various data in
the stream.

The packet callback has packet callbacks that is used to deliver the read
packet to application.  The callbacks also deliver error and end of stream
status to application.  It is also possible to attach additional packet
callbacks to stream so that there may be multiple receivers for one packet.
In the callback application may decide whether it wants to take the packet
or whether to pass it for the next receiver.  The callbacks can be added
with priority.


Locking in packet engine

Currently the engine lock is used only when the packet free list is accessed,
or new stream is added or removed.  The packet free list, however, is
accessed for all incoming packets.  Application free's the packet context so
the lock must later be acquired for putting the unused packet context back
to the free list.  It might be possible to later put the packet free list to
per-thread context.

Stream lock is taken everytime data is read from the underlaying stream or
sent to the underlaying stream.  In principal it would be possible to read
without locking, because we use per-thread incoming buffers but in reality
many platforms would not suppots reading and writing to the same underlaying
stream (effectively socket) at the same time.  And even if they would, some
rare race conditions with closing the stream might occur.  One way of dealing
with this would be to make sure that any stream used with SilcPacketStream
must itself be thread-safe.  In that case the locking would move to the
underlaying stream.

When reading data from stream, the lock is held during reading and during
data processing.  The lock is released when the packet is dispatched to
a packet callback.

When sending packet the lock is acquired only when actually accessing the
outgoing buffer and writing to the stream.  This includes encryption.  This
means that if two threads send to same stream the encryption is not in
parallel.
