L4Re/departure

Annotated docs/wiki/Client_Library

575:66ab00dc7dd0
11 months ago Paul Boddie Consolidated some of the notification functionality.
paul@183 1
= Client Library =
paul@183 2
paul@183 3
The filesystem client library offers abstractions and a number of layers of
paul@183 4
functionality to support interaction with [[Components|components]] and the
paul@398 5
provision of higher-level mechanisms and abstractions for file access. In
paul@398 6
particular, the client library is intended for use by a conventional C
paul@398 7
library, with the functions in the C library invoking client library functions
paul@398 8
and employing client library structures internally.
paul@183 9
paul@457 10
The client library is provided by [[Libraries#libfsclient|`libfsclient`]]
paul@457 11
within the `departure` package.
paul@427 12
paul@183 13
<<TableOfContents(2,3)>>
paul@183 14
paul@402 15
== Header Files ==
paul@402 16
paul@402 17
The following header files are pertinent to client library usage:
paul@402 18
paul@402 19
|| '''File'''           || '''Description'''                                ||
paul@402 20
|| `fsclient/client.h`  || Client functions and data structures             ||
paul@402 21
|| `systypes/fcntl.h`   || Flag values for opening operations               ||
paul@402 22
paul@183 23
== File Data Structures ==
paul@183 24
paul@183 25
Since files are accessed using file references, the `file_t` data structure is
paul@183 26
used to wrap such references and other relevant state. Thus, such structures
paul@183 27
can be broadly regarded as similar to the traditional `FILE` data structure.
paul@183 28
paul@402 29
The members of the `file_t` data structure are as follows:
paul@183 30
paul@457 31
|| '''Member'''   || '''Description'''                                      ||
paul@391 32
|| `ref`          || A reference to the component providing file content    ||
paul@183 33
|| `memory`       || The memory address of the exposed file region          ||
paul@183 34
|| `start_pos`    || The start position of the region in the file           ||
paul@183 35
|| `end_pos`      || The end position of the region in the file             ||
paul@183 36
|| `data_end`     || The amount or extent of populated data in the region   ||
paul@183 37
|| `data_current` || The offset used to track client position in the region ||
paul@183 38
|| `size`         || The total size of the file                             ||
paul@183 39
|| `object_flags` || Flags indicating support for certain file features     ||
paul@183 40
|| `can_block`    || Notification flags for blocking access to the file     ||
paul@183 41
|| `notifications`|| Notification flags set for the file                    ||
paul@391 42
|| `flags`        || The flags used to open the file                        ||
paul@402 43
|| `error`        || Any failure or error condition on the file             ||
paul@402 44
paul@402 45
Generally, these members should not be accessed directly, mostly being
paul@402 46
reserved for use by the client library itself.
paul@183 47
paul@183 48
== Client Programming Interface ==
paul@183 49
paul@183 50
The client programming interface provides functions somewhat resembling the
paul@183 51
traditional C library and low-level Unix interfaces for file access, and these
paul@183 52
functions are intended to support such traditional interfaces.
paul@183 53
paul@183 54
=== Files ===
paul@183 55
paul@402 56
Files are opened using the following function returning a file data structure:
paul@183 57
paul@183 58
{{{
paul@183 59
file_t *client_open(const char *name, flags_t flags);
paul@183 60
}}}
paul@183 61
paul@476 62
New instances of open files, employing potentially different flags, can be
paul@476 63
obtained using the following function:
paul@476 64
paul@476 65
{{{
paul@476 66
file_t *client_reopen(file_t *file, flags_t flags);
paul@476 67
}}}
paul@476 68
paul@402 69
To test whether the file was successfully open, the following function should
paul@402 70
be invoked, this returning a true (non-zero) value if the file was
paul@402 71
successfully opened:
paul@402 72
paul@402 73
{{{
paul@402 74
int client_opened(file_t *file);
paul@402 75
}}}
paul@402 76
paul@402 77
Each file should be closed using `client_close` regardless of whether the file
paul@402 78
was successfully opened.
paul@402 79
paul@402 80
==== Example ====
paul@402 81
paul@402 82
{{{
paul@402 83
file_t *file = client_open("somefile", O_RDONLY);
paul@402 84
paul@402 85
if (client_opened(file))
paul@402 86
{
paul@402 87
  /* Perform operations on file. */
paul@402 88
}
paul@402 89
paul@402 90
client_close(file);
paul@402 91
}}}
paul@183 92
paul@183 93
=== Pipes ===
paul@183 94
paul@402 95
Pipes are opened using a special function returning an error code, setting the
paul@402 96
supplied reader and writer endpoint parameters:
paul@183 97
paul@183 98
{{{
paul@183 99
long client_pipe(file_t **reader, file_t **writer, flags_t flags);
paul@183 100
}}}
paul@183 101
paul@402 102
Each returned pipe endpoint may be closed using `client_close`. If an error
paul@402 103
condition is reported using a non-zero value, the endpoints will not be
paul@402 104
retained and will therefore not need to be closed: the error condition is
paul@402 105
communicated purely via the return value.
paul@402 106
paul@402 107
==== Example ====
paul@402 108
paul@402 109
{{{
paul@402 110
file_t *reader, *writer;
paul@402 111
paul@402 112
if (!client_pipe(&reader, &writer, 0))
paul@402 113
{
paul@402 114
  /* Perform operations on pipe. */
paul@402 115
}
paul@402 116
paul@402 117
client_close(reader);
paul@402 118
client_close(writer);
paul@402 119
}}}
paul@183 120
paul@183 121
=== Closing Files and Pipes ===
paul@183 122
paul@183 123
Closing files and pipes involves a common operation:
paul@183 124
paul@183 125
{{{
paul@183 126
void client_close(file_t *file);
paul@183 127
}}}
paul@183 128
paul@183 129
When client programs terminate, the freeing of their object capabilities
paul@183 130
should cause the closure of files and pipes, but programs may choose to close
paul@183 131
such resources explicitly.
paul@183 132
paul@183 133
=== Reading and Writing ===
paul@183 134
paul@183 135
Reading and writing files and pipes involves functions resembling the
paul@183 136
traditional low-level `read` and `write` functions:
paul@183 137
paul@183 138
{{{
paul@183 139
offset_t client_read(file_t *file, void *buf, offset_t count);
paul@183 140
offset_t client_write(file_t *file, const void *buf, offset_t count);
paul@183 141
}}}
paul@183 142
paul@183 143
=== Navigation in Files ===
paul@183 144
paul@183 145
Support for navigation in files is provided using functions resembling the
paul@183 146
traditional higher-level `fseek` and `ftell` functions:
paul@183 147
paul@183 148
{{{
paul@183 149
offset_t client_seek(file_t *file, offset_t offset, int whence);
paul@183 150
long client_tell(file_t *file);
paul@183 151
}}}
paul@183 152
paul@183 153
=== Accessing Exposed Memory Regions ===
paul@183 154
paul@183 155
Although the client library (and the provision of files) employs mapped
paul@183 156
memory, a function can be used to explicitly reference memory for file access:
paul@183 157
paul@183 158
{{{
paul@402 159
void *client_mmap(file_t *file, offset_t position, offset_t length,
paul@402 160
                  offset_t start_visible, offset_t end_visible,
paul@402 161
                  l4re_rm_flags_t region_flags);
paul@183 162
}}}
paul@183 163
paul@402 164
Here, a portion of the indicated `file` is exposed in a memory region, with
paul@402 165
the memory region corresponding to the contents of the file starting at the
paul@402 166
given `position` in the file and having a span of the given `length` in bytes.
paul@402 167
paul@402 168
Additional parameters control masking of the file content. If `start_visible`
paul@402 169
and `end_visible` are both non-zero, then they indicate a visible span in the
paul@402 170
file. Such limits are applied to the mapped region, with locations
paul@402 171
corresponding to positions before `start_visible` and positions after
paul@402 172
`end_visible` yielding zero byte values.
paul@402 173
paul@402 174
The `region_flags` indicate the memory access properties of the mapped region
paul@402 175
in the task obtaining it. For example, where a region will be populated with
paul@402 176
code, the `L4RE_DS_F_RX` flag would be appropriate, this indicating that the
paul@402 177
region will be read and also have its contents run or executed.
paul@402 178
paul@402 179
'''Note:''' Currently, when masking is applied to a file, any write operations
paul@402 180
will cause the modified memory to be copied and modified, as opposed to
paul@402 181
causing modifications to the underlying file content. This behaviour may
paul@402 182
eventually be more configurable.
paul@402 183
paul@402 184
=== Accessing Pipe Content in Memory Regions ===
paul@402 185
paul@183 186
Pipes support a different mechanism for navigation involving the following
paul@183 187
functions:
paul@183 188
paul@183 189
{{{
paul@183 190
long client_current_region(file_t *file);
paul@183 191
long client_next_region(file_t *file);
paul@183 192
}}}
paul@183 193
paul@183 194
Such navigation functions for files and pipes do not need to be used where the
paul@183 195
higher-level reading, writing and seeking functions are in use.
paul@183 196
paul@183 197
=== Flushing and Synchronisation ===
paul@183 198
paul@183 199
For synchronisation purposes, either with the filesystem itself or with other
paul@183 200
users of the filesystem, a function resembling the traditional `fflush`
paul@183 201
function is provided:
paul@183 202
paul@183 203
{{{
paul@183 204
long client_flush(file_t *file);
paul@183 205
}}}
paul@183 206
paul@183 207
This updates the file data structure with new details of the file size, also
paul@183 208
updating any altered details of the extent of the data in the currently
paul@183 209
accessed region of the file.
paul@183 210
paul@183 211
=== Notifications ===
paul@183 212
paul@457 213
Since files and pipes may be accessed by multiple clients, this being of
paul@457 214
particular significance for any real use of pipes, notifications can be
paul@457 215
configured to communicate a change in state to other users of these resources
paul@457 216
when they are accessed. Directories can also be monitored using notifications.
paul@183 217
paul@183 218
Notification types are specified using values encoding a number of flags, and
paul@183 219
the following flags are available for this purpose:
paul@183 220
paul@183 221
|| '''Flag'''                 || '''Notification Type'''                    ||
paul@183 222
|| `NOTIFY_CONTENT_AVAILABLE` || Content available to read                  ||
paul@457 223
|| `NOTIFY_FILE_OPENED`       || File opened in directory                   ||
paul@183 224
|| `NOTIFY_PEER_CLOSED`       || Other party has closed their endpoint      ||
paul@183 225
|| `NOTIFY_SPACE_AVAILABLE`   || Space available for writing                ||
paul@183 226
paul@457 227
The delivery of notifications is requested by subscribing to notifications for
paul@457 228
a given resource via a notifier object:
paul@457 229
paul@457 230
{{{
paul@575 231
long client_subscribe(file_t *file, notify_flags_t flags, notifier_t *notifier);
paul@457 232
}}}
paul@457 233
paul@457 234
A notifier object can be common throughout all threads in a task, being
paul@457 235
obtained using the following function:
paul@457 236
paul@457 237
{{{
paul@575 238
notifier_t *client_notifier_task();
paul@457 239
}}}
paul@457 240
paul@457 241
Alternatively, a local notifier can be created for use within a thread:
paul@457 242
paul@457 243
{{{
paul@575 244
notifier_t *client_notifier_local();
paul@457 245
}}}
paul@457 246
paul@457 247
Local notifiers must be closed when they are no longer needed:
paul@457 248
paul@457 249
{{{
paul@575 250
void client_notifier_close(notifier_t *notifier);
paul@457 251
}}}
paul@457 252
paul@457 253
When notifications are no longer needed, an unsubscribe operation can be
paul@457 254
invoked:
paul@457 255
paul@457 256
{{{
paul@575 257
long client_unsubscribe(file_t *file, notifier_t *notifier);
paul@457 258
}}}
paul@457 259
paul@457 260
==== Example ====
paul@457 261
paul@457 262
{{{
paul@575 263
notifier_t *notifier = client_notifier_local();
paul@457 264
file_t *directory = client_open(filename, O_DIRECTORY);
paul@457 265
paul@457 266
if (client_opened(directory))
paul@457 267
{
paul@457 268
  if (!client_subscribe(directory, NOTIFY_FILE_OPENED, notifier))
paul@457 269
  {
paul@457 270
    if (!client_wait_file(directory, notifier))
paul@457 271
    {
paul@457 272
      /* File opened in directory. */
paul@457 273
    }
paul@457 274
  }
paul@457 275
}
paul@457 276
paul@457 277
client_close(directory);
paul@457 278
client_notifier_close(notifier);
paul@457 279
}}}
paul@457 280
paul@183 281
=== Blocking Operations ===
paul@183 282
paul@183 283
Reading and writing operations can be configured to block if data cannot be
paul@183 284
read or written respectively. The following function is provided for this
paul@183 285
purpose:
paul@183 286
paul@183 287
{{{
paul@183 288
long client_set_blocking(file_t *file, notify_flags_t flags);
paul@183 289
}}}
paul@183 290
paul@183 291
For pipes, blocking behaviour is the default and must be disabled explicitly,
paul@183 292
either by opening using the `O_NONBLOCK` flag or by calling
paul@183 293
`client_set_blocking` with no flags set.
paul@457 294
paul@457 295
Blocking behaviour is supported using the notification functionality. When
paul@457 296
access to a file or pipe cannot be satisfied for a particular operation, such
paul@457 297
as reading not being able to yield more content or writing not being able to
paul@457 298
submit more content, the task-level notifier will be used to wait for
paul@457 299
notifications applicable to the file or pipe involved. Consequently, the
paul@457 300
access will effectively block until notifications are delivered indicating
paul@457 301
that the state of the file or pipe has changed, and until it is determined
paul@457 302
that the change of state will allow the operation to proceed successfully.