5.10. Notes on event loop APIs
5.10.1. Single request - single function call
To start, we first need to establish that Anjay LwM2M Client API assumes a
single-threaded mode of operation. It relies on the user calling
anjay_serve()
whenever some packet is received and anjay_sched_run()
regularly, as described in the previous chapter.
Both of the methods block until the incoming message or (respectively) a job is
processed.
An incoming message may either be fully contained in a single packet, or split
between multiple packets, however anjay_serve()
is called just for the
first one. Other parts (if any) are fetched internally by the library. In other
words: anjay_serve()
blocks until the message gets handled completely.
When considering a request from the LwM2M Server, the block-wise transfer may be initiated either by server (e.g. a big Write request) or by the client (e.g. a large response to a Read request).
Similar situation arises when the client attempts to send a Register or Update
LwM2M request to the server with a large list of available Object Instances,
or a big Notify message. The difference is that the client sends its own
requests from within anjay_sched_run()
call instead of anjay_serve()
.
Because anjay_serve()
blocks after a packet arrives, the library can
handle at most one LwM2M Server at time, which makes its usage convenient,
as one does not have to worry about data model being accessed or modified
by multiple LwM2M Servers at the same time. Unfortunately it may happen to
be a problem, as during blockwise transfers the library is unable to respond
to other LwM2M Servers with anything else than 5.03 Service Unavailable.
Before getting worried about it too much, one shall realize that the above behavior happens only when a blockwise transfer is issued on some part of the data model - i.e. for that to become a problem one would have to store and transfer big amounts of data regularly through LwM2M which, in context of resource constrained environments targeted by the LwM2M protocol might not be the best fit.
Note
The blocking behavior does not apply to firmware downloaded using the PULL method. See Firmware transfer for details.
5.10.2. Transactions and anjay_serve()
Our data model supports transactional operations. They are here to ensure that whenever something goes wrong during a transaction, all changes applied since its beginning can be reverted - keeping the LwM2M Client in a consistent state.
As we already know, calling anjay_serve()
corresponds to processing a
single LwM2M request. This, along with properly implemented transaction
handlers guarantees that if the LwM2M Client was in a consistent state
before request had been received, then it will remain in a consistent state
after the request is processed. Moreover, because of single-threaded mode of
operation no other LwM2M Server can see the LwM2M Client being in partially
consistent state.
Things work a bit different during the Bootstrap Sequence though. When the
Client/Server initiated Bootstrap begins, the library fires transaction
handlers for all data model entities. At the same time, it enters the state
where requests originated from Bootstrap Server only are handled - there may be
more than one such request, and so anjay_serve()
could get called multiple
times. This again does not hurt consistency in any way, because according to
the LwM2M Specification, the LwM2M Client may ignore other servers during that
special time, and the library is doing just that - meaning
that they won’t be able to observe intermediate initialization state.
After the Bootstrap Sequence finishes the library checks that the data model is valid, and if it isn’t the previous correct state will be restored, which proves the point.
5.10.3. Notifications
Anjay uses its scheduler to track pending notifications. Whenever
a notification has to be sent, it is done from within anjay_sched_run()
function.
Note
Calling anjay_notify_changed()
or anjay_notify_instances_changed()
does not send notifications immediately - they use the scheduler instead.