Components
In Legato, a component is a collection of code and other files that are functionally related, along with one or more licenses that apply to the components' files.
Components can be "built" generating output like object code libraries and other files. These files can be incorporated into applications and run on a target device.
Legato components can also have language-independent interfaces and configuration data.
Rationale
It's important to manage complexity. The more complex a system becomes, the harder it is to ensure required behavior without unwanted effects. However, the more complex the problem, the more complex the software, and there's no avoiding this necessary complexity. But, if complexity is carefully compartmentalized (broken into smaller pieces with like behavior requirements), human beings can still manage it.
Many approaches have been devised to reduce complexity like 'divide-and-conquer' where the system is broken down into small, manageable parts that together provide the complete system behavior. It's proven that decoupling parts from each other (minimizing interconnectedness) can dramatically improve correctness and maintainability. Modularization and packaging support this.
Component-based software development separates implementation from interface, allowing software components to be swapped-out similar to hardware components E.g., a client component can be written to access a standard telephony interface without knowing the implementation of the interface; and this client can be used with different implementation components, depending on the underlying telephony network (cellular, PSTN, VoIP, etc.) the product supports.
Legato is designed to make it easier to build systems using a component-based model.
Decoupling components and hiding implementation details within components (behind their interfaces) has a major impact on maintainability:
- Clearly defining interface and components' roles and responsibilities allow defect sources to be isolated more easily.
- Components can be swapped out with stubs, and interfaces can be instrumented to gather more troubleshooting information.
- Component changes that don't affect its interfaces are constrained to that component. This supports
refactoring.
- Test suites can be applied to components implementing the same interface so test reuse is also supported.
Component-based software development also enables creative interconnection of components in previously unimagined ways to discover unique new solutions.
Components designed for reuse with simple, clean interfaces using standard data types and methods can be recombined into different arrangements that exhibit different overall system behaviors. Web mashups can combine previously-constructed web services into completely new applications because those web services provide well-defined interfaces using standard access mechanisms and data formats. The mashup doesn't care about how the web services are implemented; the mashup only cares about what the interfaces provide.
A further potential of component-based development is the ease of sharing (for free or for pay) of these reusable components. If a component is very useful to others, it can be shared and reused in accordance with the license that the author has chosen to release it under.
Legato Components
In the Legato world, each component is kept in its own directory. The name of the directory is also the name of the component.
The component can contain any number of source code files, pre-compiled binary files, resource files (e.g., audio files or images), or whatever files the component needs.
Each component must have a Component.cdef
file. This file describes the component to the Legato Build Tools. It lists what source files the component includes, what interfaces it uses or implements, what resource files it includes, and what files it needs in the target host runtime environment.
Components that have source code files can be included in executables inside apps. The build tools will generate the appropriate start-up code for the executable to ensure that the component gets initialized when the executable is started.
Component Programming Model
Generally, Legato components are written using an event-driven model. Callback functions called "event handlers" (or "handlers" for short) are attached to specific events, and when events occur, the thread's event loop calls the appropriate handlers.
Every component implements a special initialization function (COMPONENT_INIT
in C). That function does whatever initialization is required for that component (e.g., initializes data structures, registers event handlers, starts threads, etc.) and returns. The process's main thread (auto-generated by the Build Tools) runs all the component initializers for all components in the executable and then enters the Legato event loop. The event loop then reacts to events by and calling registered handlers.
This model allows multiple components to be deployed to the same process and even share a single thread within that process, without having to know anything about each other. This reduces the risk of multi-threaded race conditions and saves stack memory space, while simplifying many programming tasks.
Sometimes the judicious use of threads can considerably simplify a program, so multi-threading is also supported by Legato, along with the usual thread synchronization tools (mutexes, semaphores, etc.) and the addition of queued function calls.
If a component blocks a shared thread, it blocks every component sharing that thread. So, to maximize a component's re-usability in different programs, it's best to avoid blocking the main thread. Legato components can start their own, private threads to avoid this. And any thread can run its own event loop if an event-driven thread with blocking event handlers is desired.
Component Initialization
On targets running real-time operating systems (RTOS) there is a need for special initialization for components which are included in more than one executable. On RTOS these will all be instances of the same component, so ordinary global or static variables will be shared across all instances of the component. The Legato Component Data API provides a method to create per-component instance data (see le_cdata.h). To initialize per-component instance data COMPONENT_INIT is called in the context of event loop of every executable. To initialize data shared across all instances of the component another special function (COMPONENT_INIT_ONCE
in C) is provided. This function is called only once in the context of Supervisor main thread before call(s) to COMPONENT_INIT.
On targets running Linux OS COMPONENT_INIT_ONCE is not required since all component instances have their own copies of data, but provided for code compatibility. It acts the same as COMPONENT_INIT, but is called before the event loop is started.
Legacy Code
Sometimes you'll have a legacy program you want to include in your app without rewriting it in the style of a Legato component. This can be done by building your legacy app using your normal build system (but using the appropriate cross build toolchain provided for your target device) and bundling the built programs and libraries into the app.
Copyright (C) Sierra Wireless Inc.