7.2. Threading model¶
All operations on different Connector()
instances are thread-safe.
Operations on the same Connector()
instance or any contained
Input()
or Output()
are, in general, not protected for
multi-threaded access. The only exceptions are the following wait operations
with the caveats mentioned below.
Note
If you are using the event-based functionality
(e.g., connector.on(on_data_available, () => {})
),
refer to the additional restraints described in the
Additional considerations when using event-based functionality
section below.
- Thread-safe operations:
Connector.wait()
(wait for data on anyInput
)Output.wait()
(wait for acknowledgments)Output.waitForSubscriptions()
(wait to (un)match a subscription)Input.wait()
(wait for data on thisInput
)Input.waitForPublications()
(wait to (un)match a publication)
Note
All of the operations listed above are asynchronous (and return a Promise
which will eventually be resolved or rejected).
Note
Output.write()
can block the current thread under certain
circumstances, but Output.write()
is not thread-safe.
Warning
Input.wait()
and Input.waitForPublications()
are not reentrant
(it is currently not possible to have more than one Promise
pending on
Input.wait()
or Input.waitForPublications()
). Since internally
the same resource is used for both of these operations, it is not possible
to wait on both Input.wait()
or Input.waitForPublications()
simultaneously.
For example, the following code will throw an DDSError()
:
const waitForDiscovery = async () => {
try {
await input.waitForSubscriptions()
} catch (err) {
console.log('Caught error: ' + err)
}
}
const wait = async () => {
try {
await input.wait()
} catch (err) {
console.log('Caught error: ' + err)
}
}
waitForDiscovery()
wait()
The input.wait
call within the asynchronous function wait
will
fail since there is a simultaneous request to input.waitForSubscriptions
.
This can be avoided by ensuring you only have a single wait
operation
pending at a time:
const waitForDiscovery = async () => {
try {
await input.waitForSubscriptions()
} catch (err) {
console.log('Caught error: ' + err)
}
}
const wait = async () => {
try {
await input.wait()
} catch (err) {
console.log('Caught error: ' + err)
}
}
const myApplication = async () => {
await waitForDiscovery()
await wait()
}
myApplication()
The same limitation exists between Output.wait()
and
Output.waitForSubscriptions()
.
7.2.1. Additional considerations when using event-based functionality¶
If using event-based notifications (that is, if you have installed a listener
for the on_data_available
event on a Connector()
, as explained in
Reading or taking the data), there are additional restrictions to
be aware of.
It is possible to install multiple listeners for the
on_data_available
event:
connector.on('on_data_available', () => {
// Read the samples so that they remain available within the Input
input.read()
doSomething(input.samples)
})
connector.on('on_data_available', () => {
// Take the samples to remove them from the Input. Since event callbacks
// are, by default, run in the order they are registered, there is no risk
// that this is run before the above listener
input.take()
doSomethingElse(input.samples)
})
In the above example, when data is received, both the installed callbacks will be run. These callbacks are run sequentially, so it is not necessary to protect them manually at an application level.
It is not possible to call Connector.wait()
if there is an installed
listener for the on_data_available
event. This is due to the fact that
while the on_data_available
listener is installed, the resource required
internally for Connector.wait()
is busy.
In your application, if you remove all the registered listeners for
on_data_available
and later need to re-add them (or wait for data using
Connector.wait()
), it is necessary to call
Connector.waitForCallbackFinalization()
. This method returns a
Promise that will resolve once the resources used internally by the
Connector()
are no longer in use:
connector.on('on_data_available', handleDataCallback)
connector.off('on_data_available', handleDataCallback)
// Since we removed the only callback for the on_data_available event, we must
// now wait for the Promise to resolve before re-adding a new callback
await connector.waitForCallbackFinalization()
connector.on('on_data_available', newHandleDataCallback)
Warning
It is important to note that Connector.waitForCallbackFinalization()
does not free any resources. It should only be used for notification of
when a Connector()
can be re-used for other wait operations. It is
still necessary to call Connector.close()
to free the resources.
If you install a on_data_available
listener, you need to wait for the
Promise
returned by Connector.close()
to resolve before continuing
with the application shutdown procedure. This allows the Connector()
to synchronize its shutdown with the listener:
connector.close()
.then(() => {
// continue with application shutdown
})