1 = Servers = 2 3 Server code is code that provides components in the form described by those 4 components' interfaces, employing interprocess communications mechanisms to 5 expose those components to [[Clients|clients]]. 6 7 <<TableOfContents(2)>> 8 9 == Introduction == 10 11 The following example interfaces will be used to illustrate the mechanisms 12 described in this document. Firstly, in `calc.idl`: 13 14 {{{ 15 interface Calc 16 { 17 void add(in int left, in int right, out int result); 18 void subtract(in int left, in int right, out int result); 19 void multiply(in int left, in int right, out int result); 20 void divide(in int numerator, in int denominator, out int result); 21 }; 22 }}} 23 24 Secondly, in `counter.idl`: 25 26 {{{ 27 interface Counter 28 { 29 void increment(out int result); 30 }; 31 }}} 32 33 Finally, in `calc_counter.idl`: 34 35 {{{ 36 import "calc.idl"; 37 import "counter.idl"; 38 39 interface MappedFileObject composes Dataspace, File, Flush, MappedFile, Notification; 40 }}} 41 42 To expose these interfaces to other programs, a server program would do the 43 following in each case: 44 45 * Obtain appropriate program types to reference the interface 46 47 * Initialise values or objects that reference the component implementation 48 49 * Interact with interprocess communication mechanisms and request the 50 invocation of the component's operations 51 52 The sections below describe how this is done in the different supported 53 programming languages. 54 55 Note that the mechanism by which interface descriptions are processed for use 56 in programs is described in the [[L4Re Support]] document. The `idl` manual 57 page provides details for developers wishing to use the tool directly. 58 59 == Server Header Files == 60 61 Given the existence of generated files for the interface, a program would 62 include the server header file: 63 64 {{{ 65 #include "calc_server.h" 66 }}} 67 68 To expose components as servers, a `libipc` header file is also needed: 69 70 {{{ 71 #include <ipc/server.h> 72 }}} 73 74 == C Language Servers == 75 76 A server exposing the `Calc` interface will employ an object having the `Calc` 77 type. This encapsulates a reference to the object state and a reference to the 78 interface details. 79 80 Since the aim in implementing a server is to provide access to state 81 information held within a process, the object state reference will refer to a 82 location holding such information via a pointer member. The following could be 83 used for the state supporting the `Calc` interface. 84 85 {{{ 86 ref_Calc ref = {.ptr=0}; 87 }}} 88 89 Here, a value of `0` is used because the interface operations are stateless 90 (they act like plain functions), and so it does not really matter what `ptr` 91 is set to. 92 93 The `Calc` object is initialised using the chosen reference value and a 94 reference to interface information: 95 96 {{{ 97 Calc obj = {.ref=ref, .iface=&server_iface_Calc}; 98 }}} 99 100 Unlike in the client initialisation where a predefined interface already 101 exists, it is up to the component implementer to define the functions that 102 support the exposed interface. For example: 103 104 {{{ 105 iface_Calc server_iface_Calc = { 106 .add=calc_add, 107 .subtract=calc_subtract, 108 .multiply=calc_multiply, 109 .divide=calc_divide, 110 .pow=calc_pow 111 }; 112 }}} 113 114 The `Calc` object is then [[#Exposing|associated with a server capability]] 115 and invoked when incoming messages are directed towards it. 116 117 Where operations are not stateless and instead operate on some kind of state 118 or object, the reference is initialised as in this example involving the 119 `Counter` interface: 120 121 {{{ 122 int counter = 0; 123 ref_Counter ref = {.ptr=&counter}; 124 }}} 125 126 Here, the state modified by the `Counter` operations is a simple integer. As 127 long as the operations are written to interpret the state correctly, it does 128 not matter what kind of object provides the state. For C language servers, 129 there is not necessarily a requirement to have a dedicated data structure 130 containing the state. 131 132 === Compound Interfaces === 133 134 When supporting multiple interfaces, the general initialisation resembles that 135 shown above. Firstly, the object state reference is defined, as in this 136 example involving a reference suitable for a `CalcCounter` object: 137 138 {{{ 139 ref_CalcCounter ref = {.ptr=0, .as_Counter={.ptr=&counter}}; 140 }}} 141 142 Here, the pointer member is given as `0` because there is no dedicated state 143 for the `CalcCounter` compound interface. However, when the component is to be 144 interpreted as a `Counter`, the `as_Counter` member provides the corresponding 145 reference, this indicating the address of a counter as the pointer to the 146 appropriate state, as previously suggested. 147 148 The object employed by the server is itself populated in the same way as shown 149 above: 150 151 {{{ 152 CalcCounter obj = {.ref=ref, .iface=&server_iface_CalcCounter}; 153 }}} 154 155 The principal difference between simple and compound interface definition 156 involves the interface details. With a compound interface, there must be a way 157 to address the individual interfaces, and this is done using members employing 158 a specific naming convention: 159 160 {{{ 161 iface_CalcCounter server_iface_CalcCounter = { 162 .to_Calc=&server_iface_Calc, 163 .to_Counter=&server_iface_Counter 164 }; 165 }}} 166 167 Here, the `to_Calc` member provides a reference to suitable details for the 168 `Calc` interface, and the `to_Counter` member provides the corresponding 169 reference for the `Counter` interface. 170 171 === Operation Implementations === 172 173 Implementations of operations employ a signature where the first parameter is 174 the object state reference value. This permits access to the state information 175 and allows functions to update the state of several objects independently: 176 177 {{{ 178 long counter_increment(ref_Counter _self, int *result) 179 { 180 int *counter = (int *) (_self.ptr); 181 182 *counter = *counter + 1; 183 *result = *counter; 184 return L4_EOK; 185 } 186 }}} 187 188 Here, the pointer member is used to access the information referenced when the 189 object was initialised. As noted above, the interpretation of the pointer 190 member is left to the operations, with the chosen interpretation being a 191 simple integer in this example. 192 193 == C++ Language Servers == 194 195 An object representing the component will have the `Calc` type. An object 196 providing a specific implementation of the component might have a type called 197 `server_Calc` and be initialised as follows: 198 199 {{{ 200 server_Calc obj; 201 }}} 202 203 Such an object type would need to be a subtype of `Calc` and would resemble 204 the following: 205 206 {{{ 207 class server_Calc : public Calc 208 { 209 public: 210 long add(int left, int right, int *result); 211 long subtract(int left, int right, int *result); 212 long multiply(int left, int right, int *result); 213 long divide(int numerator, int denominator, int *result); 214 }; 215 }}} 216 217 The `server_Calc` object is then [[#Exposing|associated with a server 218 capability]] and invoked when incoming messages are directed towards it. 219 220 === Compound Interfaces === 221 222 When supporting multiple interfaces, the general initialisation resembles that 223 shown above, as in this example involving a specific object type derived from 224 the `CalcCounter` type: 225 226 {{{ 227 server_CalcCounter obj; 228 }}} 229 230 Similarly, a definition of this type is required supporting the range of 231 operations associated with all of the individual interfaces: 232 233 {{{ 234 class server_CalcCounter : public CalcCounter 235 { 236 int counter = 0; 237 238 public: 239 long add(int left, int right, int *result); 240 long subtract(int left, int right, int *result); 241 long multiply(int left, int right, int *result); 242 long divide(int numerator, int denominator, int *result); 243 long increment(int *result); 244 }; 245 }}} 246 247 === Operation Implementations === 248 249 Since C++ manages access to object state transparently, the way of accessing 250 such state is more straightforward: 251 252 {{{ 253 long server_CalcCounter::increment(int *result) 254 { 255 counter = counter + 1; 256 *result = counter; 257 return L4_EOK; 258 } 259 }}} 260 261 ((Exposing)) 262 == Exposing Objects as Servers == 263 264 In L4Re, component objects can be made available to other programs by 265 associating them with interprocess communication (IPC) "gate" capabilities and 266 then waiting for incoming messages. 267 268 Given an existing capability accessible via the program environment, a 269 component can be exposed to other programs as follows: 270 271 {{{ 272 ipc_server_loop_for(Calc, &obj, "server"); 273 }}} 274 275 This convenience function (or, more accurately, macro) binds the main thread 276 to the named capability. This also handles the details of waiting for messages 277 and directing requests to the component, with the following parameters being 278 specified: 279 280 * The interface type involved 281 282 * The object address for the component 283 284 * The named capability through which incoming messages will be received 285 286 === Technical Details === 287 288 This macro invocation expands to a call to `_ipc_server_loop_for` with the 289 following parameters: 290 291 * The default server configuration for the `Calc` interface, generated 292 automatically and provided by `config_Calc` 293 294 * The object address for the component 295 296 * The named capability through which incoming messages will be received 297 298 Here, the macro casts the object address to the appropriate type. In simple 299 cases, this is a superfluous operation since the object type will be directly 300 compatible with the interface type used to expose the object, and such a 301 casting operation will not change the address. However, in more complicated 302 cases such as where an object type inherits from the interface type and one or 303 more other types, such a casting operation may change the address. 304 305 It is important to note that the IPC handling mechanism treats the object 306 address as an opaque value, presenting it to the handler function for 307 interpretation as a pointer to an object of the interface type. Where the 308 object type is directly compatible (such that casting to the interface type 309 does not change the address), this reinterpretation of the address will be 310 correct. Where the type is not directly compatible, however, (such that 311 casting to the interface type does change the address) the reinterpretation is 312 incorrect: subsequent accesses via the pointer will be erroneous. 313 314 Consequently, it is essential to perform casting to the interface type before 315 the address is presented to the IPC handling mechanism. The macro is provided 316 to ensure that this step is not overlooked. 317 318 == Example Code == 319 320 The `pkg/idl4re-examples` directory contains some example packages for L4Re 321 that feature some of the above code and demonstrate the techniques involved. 322 The `idl4re-examples` directory can be copied into the L4Re distribution's 323 `pkg` directory and built as follows: 324 325 {{{ 326 make O=mybuild S=pkg/idl4re-examples 327 }}} 328 329 The `mybuild` directory name should be adjusted to match your own choice of 330 build output directory.