Thanks. To give an example - there is a (ref {}) mapping from IDs to
clients. On read events the request ist read and the client is
retrieved from this map by its id and handed over to the state machine
which evaluates the request and takes the client state into account.
It invokes callback-handler depending on request type which do some
processing and return some data. The client state is then updated and
a response is send back. In parallel there are cleanup-threads running
over the map which also access and possibly change clients (usually
some timeout stuff).
Some pseudo-code:
(def clients (ref {}))
(defn handle-request [client req]
(binding [protocol/client client
protocol/callback1 callback1
...
protocol/callbackN callbackN]
(update clients (:id client) (protocol/process req)))) ; updates
clients with new client returned from protocol/process
(defn on-read []
(let [req (read-request)
id (read-id)
client (@clients id)] ; omitted client creation if not found
(on-thread (handle-request client req))))
(defn clean-up [] ; run in separate thread
(doseq c (vals @clients))
(check-and-change c)))
in protocol
(defn some-dispatch [t data]
(send-response (callback1 data)) ; callback1 one does arbitrary
processing including IO
(update client new-state))
(defn process [req]
(some-dispatch (determine-request-type req) (extract-info req)))
I thought about agents as well but since they run asynchronously and I
need synchronous change I am not sure if they fit in here very well. I
omitted any transaction/locking code but since the protocol and
possibly the callback code do IO I can only think of locking the
client to prevent concurrent modifications, e.g. from clean-up. To use
STM I would need to factor out the IO code from protocol and callbacks
it seems.
Thanks!