pub struct Server { /* private fields */ }Expand description
The context shared by the classes and the devices of a server, which every class of it
owns, and which reaches what owns them, its Core, weakly.
§Ownership
┌───────────────────────────┐ ┌─────────────────┐
│ ServerHandle │ │ DevProxy │
└─────────────┬─────────────┘ └─────┬───────────┘
│ │
│core │backend: BackendDev
│1 │0..1
┌─────────────────────┴─────────────────────┐ 1┌───────────────────┐ │
│ Core ├───────┤ dyn BackendServer │ │
└─┬──────┬──┬───┬──────┬────┬─────┬─────────┘backend└─────────┬──────┬──┘ │
│ name │ │ │ name │ │ ╎1 │ name │ │
╰──┬───╯ │ ╰──┬───╯ │ ╎core ╰──┬───╯ │
│ │ │ server│ ╎ │ │
│ │ │ 1│ ╎ │ │
│ │ │ ┌───┴─────┴───────┐ │ │
│ │ │ │ Server │ │ │
│ │ │ └───────────────┬─┘ │ │
│ │ │classes: AnyClass │1 │ │
│ │ │0..1 │server │ │
│ │ ┌────┴────────────────────┴───────┐ │ │
│ │ │ Class<M> │ │ │
│ │ └─────────────────────────┬───────┘ │ │
│ │ │1 │ │
│ │ │class │ │
devs: AnyDev│ │pending: AnyDev │ devs: BackendDev│ │
0..1│ │* │ 0..1│ │
┌─────┴──────┴───────────────────────────┴────────────────────────┴──────────────┴─────────┐
│ Dev<M> │
└─┬──────┬─┬──────┬──┬────────────┬─────────────┬─────────────────────┬───────────┬────────┘
│ name │ │ name │ │ │ │ 0..1╎ ╎0..1
╰──┬───╯ ╰─┬────╯ │ │ │ dev: BackendDev╎ ╎dev: BackendDev
│ │ │ │ │ ╎ ╎
commands│ │attrs │state_attr │status_attr │ ╎ ╎
0..1│ │0..1 │1 │1 │ ╎ ╎
┌───────────┴┐ ┌───┴───────┴────────────┴────────┐ │ ╎ ╎
│ Command<M> │ │ Attr<M> │ │ ╎ ╎
└────────────┘ └─────────────────────────────────┘ │local_handle ╭───┴───╮ ╭─────┴──────╮
│0..1 │ itr │ │ port, name │
┌───────┴────────┐ 1┌─┴───────┴─┴────────────┴─┐
│ LocalDevHandle ├╌╌╌╌╌╌┤ local registry │
└────────────────┘ keys└──────────────────────────┘A solid line is an Arc, a dashed one a Weak. An end of a line has a
multiplicity, and the name of the field holding it, only when the class at the other end
reaches it: an end without one is not reached through that line. A field holding the
other end as a trait object is written field: Trait. A rounded box is the key of a map
of the class it hangs from, which holds at most one of the other end per key.
LocalDevHandle holds the keys of the entries of its device, which it takes out of the
registry when dropped, and a weak reference to the device, not drawn, which only tells
its entries from those of a device registered since.
The backend is given each device as a dyn BackendDev, which is the DevImpl<M>
wrapper of a Dev<M>. A DevProxy holds the backend it
reaches its device through as a dyn BackendDev too, which is the device itself when it
is served by this process and found in the registry, and a client of the protocol of the
backend otherwise.
The commands and attributes of a device, kept in its mutable field, each hold their
definition, not drawn, which is shared with the ClassDef of its class.
Every solid line points down from the handle: the only links going back up are the weak
core field and those of the local registry. Dropping the handle stops the backend, and
frees everything once nothing else holds the core, which a request running a
Self::sync_async or a Self::stop_async does until it ends, see ServerHandle.
The local registry only ever holds devices weakly, so it never keeps one alive, while a
client of this process holds the device it found for as long as it is connected to it,
which refuses its requests once withdrawn, see Dev::is_withdrawn. A device is taken
out of the registry, by dropping its LocalDevHandle, before it is removed from the
backend, so that local clients stop finding it first.
Implementations§
Source§impl Server
impl Server
Sourcepub fn name(&self) -> &str
pub fn name(&self) -> &str
The exe/inst name of that server, under which it is registered in the database.
Sourcepub fn admin_dev_name(&self) -> &str
pub fn admin_dev_name(&self) -> &str
The name of the admin device of that server, dserver/exe/inst.
Sourcepub fn database(&self) -> &DatabaseProxy
pub fn database(&self) -> &DatabaseProxy
The database that server is registered in.
Sourcepub fn backend(&self) -> Option<Arc<dyn BackendServer>>
pub fn backend(&self) -> Option<Arc<dyn BackendServer>>
The backend serving the devices of that server, None once it is not served
anymore, see ServerHandle.
Sourcepub fn add_class<M: Model>(&self, def: ClassDef<M>) -> Result<Arc<Class<M>>>
pub fn add_class<M: Model>(&self, def: ClassDef<M>) -> Result<Arc<Class<M>>>
Register a new class in this server, from its definition.
Sourcepub fn get_class<M: Model>(&self, name: &str) -> Option<Arc<Class<M>>>
pub fn get_class<M: Model>(&self, name: &str) -> Option<Arc<Class<M>>>
Get a class of this server from its name, if it is one of that model.
Sourcepub fn add_dev(&self, class: &str, name: &str) -> Result<()>
pub fn add_dev(&self, class: &str, name: &str) -> Result<()>
Add a device to this server, as an instance of the class of that name, which is how
the devices listed by the database are added, the database giving the class of each
by its name. See Class::add_dev for the typed form.
The device is only registered: it is initialized, served and exported by the next
Self::sync_async.
Sourcepub fn remove_dev(&self, name: &str) -> bool
pub fn remove_dev(&self, name: &str) -> bool
Remove the device of that name from this server, giving back whether it had one.
Its name is free again right away, but the device itself is only unexported, no
longer served and deleted by the next Self::sync_async. A device removed before any
synchronization reached it is simply dropped.
Sourcepub async fn sync_async(&self) -> Result<()>
pub async fn sync_async(&self) -> Result<()>
Apply what was done to the devices of this server since the last synchronization: the devices removed are unexported, no longer served and deleted, then the devices added are initialized, served and exported, which is what makes them reachable.
The devices added are initialized one after the other, class by class in the order the classes were made, and in the order they were added within a class. A device whose initialization adds devices has them initialized by this same synchronization, which is how the admin device boots a server. Each device is then served and exported on its own, which is what a Tango server always tells the database.
A device whose initialization failed is still served and exported, in FAULT, so that a client can ask it what went wrong. A device that could not be served or exported is retried by the next synchronization, and the first such error is given back once every other device is done.
Synchronizations never interleave: one waits for the one running to be done, so that everything done before calling it is applied once it returns. This is also why an init function must never call it, since it would wait for itself.
Sourcepub fn sync(&self) -> Result<()>
pub fn sync(&self) -> Result<()>
The blocking form of Self::sync_async, which must therefore never run on the
executor of a backend, in an asynchronous handler in particular.
Sourcepub fn class_names(&self) -> Vec<Arc<str>>
pub fn class_names(&self) -> Vec<Arc<str>>
The names of every class of this server, in the order they were made, the admin one first.
Sourcepub fn dev_names(&self) -> Vec<Arc<str>>
pub fn dev_names(&self) -> Vec<Arc<str>>
The names of every device of this server, class by class in the order of
Self::class_names, and by name within a class.
Sourcepub fn get_dev<M: Model>(&self, name: &str) -> Option<Arc<Dev<M>>>
pub fn get_dev<M: Model>(&self, name: &str) -> Option<Arc<Dev<M>>>
Get a device of this server from its name, if its class is one of that model.
Sourcepub async fn stop_async(&self) -> Result<()>
pub async fn stop_async(&self) -> Result<()>
Stop this server, which is only ever done once: it is unexported as a whole, so
that clients stop finding it, then its backend is stopped, the requests in flight
still being answered, and its devices are deleted, see Self::wait for what waits
for the backend to be through. No synchronization runs after this, the devices
still waiting for one never being served.
The devices are deleted one after the other, the classes in the reverse of their
order, the admin device last, each once the requests still running on it are
through, see Dev::is_withdrawn: a request that never ends therefore keeps this
from ever returning. This is also why a request handler must never call it, since
it would wait for itself; the Kill command of the admin device stops its server
from the pool instead.
The backend is stopped and the devices deleted even if the server could not be unexported, and the first error met is given back.
Sourcepub fn stop(&self) -> Result<()>
pub fn stop(&self) -> Result<()>
The blocking form of Self::stop_async, which must therefore never run on the
executor of a backend, in an asynchronous handler in particular.
Sourcepub async fn wait_async(&self)
pub async fn wait_async(&self)
Wait for this server to be stopped, see Self::stop_async, which is what the
thread running a server spends its life in: for its backend to be through with
every request, and for its devices to be deleted. A server that is not served
anymore, see ServerHandle, is not waited for.
The backend is waited for first, which is only through once stopped, see
BackendServer::wait, then the stop itself, which deletes the devices under the
lock of the synchronizations. This waits for every request in flight, and must
therefore never be called from a request handler, which would wait for itself.
Sourcepub fn wait(&self)
pub fn wait(&self)
The blocking form of Self::wait_async, which must therefore never run on the
executor of a backend, in an asynchronous handler in particular. See
Self::wait_timeout for a wait that gives up.
Sourcepub fn wait_timeout(&self, timeout: Duration) -> Result<()>
pub fn wait_timeout(&self, timeout: Duration) -> Result<()>
Wait for this server to be stopped, see Self::wait_async, giving up with
ErrorKind::TimedOut if the backend is still answering requests once the timeout
has run out, which a request that never ends makes it, or a server never stopped.
The timeout only bounds the backend: once it is through, the deletion of the devices is waited for, however long that takes.