L4Re/departure

Annotated libnotifier/lib/src/notifier.cc

548:2dd97d71e627
19 months ago Paul Boddie Removed the indicated notifier from unsubscribe operations since it cannot be trivially compared to any subscribed notifier. Thus, only a single notifier can be used with any given file, pipe, directory or process endpoint.
paul@124 1
/*
paul@479 2
 * Generic object event notification support.
paul@124 3
 *
paul@479 4
 * Copyright (C) 2021, 2022, 2023 Paul Boddie <paul@boddie.org.uk>
paul@124 5
 *
paul@124 6
 * This program is free software; you can redistribute it and/or
paul@124 7
 * modify it under the terms of the GNU General Public License as
paul@124 8
 * published by the Free Software Foundation; either version 2 of
paul@124 9
 * the License, or (at your option) any later version.
paul@124 10
 *
paul@124 11
 * This program is distributed in the hope that it will be useful,
paul@124 12
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
paul@124 13
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
paul@124 14
 * GNU General Public License for more details.
paul@124 15
 *
paul@124 16
 * You should have received a copy of the GNU General Public License
paul@124 17
 * along with this program; if not, write to the Free Software
paul@124 18
 * Foundation, Inc., 51 Franklin Street, Fifth Floor,
paul@124 19
 * Boston, MA  02110-1301, USA
paul@124 20
 */
paul@124 21
paul@124 22
#include <map>
paul@124 23
#include <mutex>
paul@124 24
paul@539 25
#include <l4/sys/irq.h>
paul@539 26
paul@124 27
#include <ipc/cap_alloc.h>
paul@124 28
#include <ipc/server.h>
paul@535 29
#include <resource/resource_server.h>
paul@124 30
paul@124 31
#include "notification_client.h"
paul@124 32
#include "notifier.h"
paul@534 33
#include "notifier_server.h"
paul@124 34
paul@124 35
paul@124 36
paul@177 37
/* Null notification state. */
paul@177 38
paul@479 39
static ObjectNotificationState _null_state;
paul@177 40
paul@177 41
paul@177 42
paul@177 43
/* Lock protecting per-task notifier access. */
paul@177 44
paul@177 45
static std::mutex _lock;
paul@177 46
paul@177 47
/* Per-task storage for specific waiting operations. */
paul@177 48
paul@479 49
static SpecificObjectNotifier *_notifier = NULL;
paul@177 50
paul@177 51
paul@124 52
paul@479 53
/* Return the per-task notifier for object-specific waiting operations. */
paul@177 54
paul@479 55
SpecificObjectNotifier *notifier_get_task_notifier()
paul@177 56
{
paul@177 57
  std::lock_guard<std::mutex> guard(_lock);
paul@177 58
paul@177 59
  /* Start any new notifier. */
paul@124 60
paul@177 61
  if (_notifier == NULL)
paul@177 62
  {
paul@479 63
    _notifier = new SpecificObjectNotifier;
paul@177 64
    _notifier->start();
paul@177 65
  }
paul@177 66
paul@177 67
  return _notifier;
paul@177 68
}
paul@177 69
paul@479 70
/* Return a local notifier for general object waiting operations. */
paul@124 71
paul@479 72
GeneralObjectNotifier *notifier_get_local_notifier()
paul@180 73
{
paul@479 74
  GeneralObjectNotifier *notifier = new GeneralObjectNotifier;
paul@124 75
paul@180 76
  notifier->start();
paul@180 77
  return notifier;
paul@124 78
}
paul@124 79
paul@124 80
paul@124 81
paul@180 82
/* Virtual destructor required for base class instance reference deletion. */
paul@180 83
paul@479 84
ObjectNotifier::~ObjectNotifier()
paul@180 85
{
paul@539 86
  stop();
paul@539 87
paul@537 88
  ServerConfigs::iterator it;
paul@537 89
paul@537 90
  for (it = _configs.begin(); it != _configs.end(); it++)
paul@537 91
    delete *it;
paul@537 92
paul@537 93
  _configs.clear();
paul@539 94
paul@539 95
  /* Handle deletion of the special task notifier. */
paul@539 96
paul@539 97
  if (this == _notifier)
paul@539 98
    _notifier = NULL;
paul@180 99
}
paul@180 100
paul@124 101
paul@124 102
paul@124 103
/* Start listening for notifications. */
paul@124 104
paul@479 105
long ObjectNotifier::start()
paul@124 106
{
paul@124 107
  if (_started)
paul@124 108
    return L4_EOK;
paul@124 109
paul@534 110
  /* Create a new thread to serve a "null" resource. This resource is not used
paul@534 111
     for notifications but merely for control purposes. */
paul@124 112
paul@534 113
  NotifierResource *notifier = new NotifierResource;
paul@534 114
  ResourceServer server(notifier);
paul@538 115
  long err = server.start_thread(true, false);
paul@124 116
paul@124 117
  if (err)
paul@124 118
    return err;
paul@124 119
paul@537 120
  _configs.push_back(server.config());
paul@124 121
  _started = true;
paul@124 122
paul@539 123
  /* Retain the IRQ created for the server for control purposes. */
paul@539 124
paul@539 125
  _irq = server.config()->irq;
paul@539 126
paul@124 127
  return L4_EOK;
paul@124 128
}
paul@124 129
paul@177 130
paul@177 131
paul@539 132
/* Stop the notifier. */
paul@539 133
paul@539 134
void ObjectNotifier::stop()
paul@539 135
{
paul@539 136
  if (l4_is_valid_cap(_irq))
paul@539 137
  {
paul@539 138
    l4_irq_trigger(_irq);
paul@539 139
    _irq = L4_INVALID_CAP;
paul@539 140
  }
paul@539 141
}
paul@539 142
paul@539 143
paul@539 144
paul@479 145
/* Return notification state for the given object or null state if no record
paul@479 146
   existed for the object. */
paul@177 147
paul@479 148
ObjectNotificationState &ObjectNotifier::object_state(notifiable_t *object, bool create)
paul@177 149
{
paul@479 150
  ObjectNotificationStates::iterator it = _state.find(object);
paul@177 151
paul@177 152
  if (it == _state.end())
paul@177 153
  {
paul@177 154
    if (create)
paul@479 155
      return _state[object];
paul@177 156
    else
paul@177 157
      return _null_state;
paul@177 158
  }
paul@177 159
paul@177 160
  return it->second;
paul@177 161
}
paul@177 162
paul@479 163
/* Subscribe to notification events on an object. */
paul@177 164
paul@479 165
long ObjectNotifier::subscribe(notifiable_t *object, notify_flags_t flags)
paul@177 166
{
paul@534 167
  /* Acquire the lock for state lookup. */
paul@534 168
paul@534 169
  std::unique_lock<std::mutex> state_guard(_state_lock);
paul@534 170
paul@534 171
  /* Obtain potentially new state for the object. */
paul@534 172
paul@534 173
  ObjectNotificationState &state = object_state(object, true);
paul@177 174
paul@534 175
  if (state.is_null())
paul@534 176
  {
paul@534 177
    /* Serve the new object in the notifier thread. */
paul@534 178
paul@534 179
    NotifierResource *resource = new NotifierResource(this, object);
paul@534 180
    ResourceServer server(resource);
paul@538 181
    long err = server.start_in_thread(_configs.front()->thread);
paul@534 182
paul@534 183
    if (err)
paul@534 184
      return err;
paul@534 185
paul@537 186
    _configs.push_back(server.config());
paul@534 187
    state.endpoint = server.config()->server;
paul@534 188
  }
paul@177 189
paul@548 190
  /* Forbid repeated subscription.
paul@548 191
     NOTE: Could instead rely on being unsubscribed, releasing the existing
paul@548 192
           endpoint. */
paul@548 193
paul@548 194
  else
paul@548 195
    return -L4_EEXIST;
paul@548 196
paul@541 197
  /* Allow this object to be re-entered. This may occur because the subscribe
paul@541 198
     operation can cause deferred notifications to be sent back to the
paul@541 199
     subscribed notifier and to this object. */
paul@541 200
paul@541 201
  state_guard.unlock();
paul@541 202
paul@483 203
  /* Subscribe, sending the notification endpoint via the principal reference
paul@483 204
     for the object. */
paul@288 205
paul@479 206
  client_Notification notify(object->base->ref);
paul@177 207
paul@534 208
  return notify.subscribe(state.endpoint, flags);
paul@177 209
}
paul@177 210
paul@479 211
/* Unsubscribe from notification events on an object. */
paul@177 212
paul@479 213
long ObjectNotifier::unsubscribe(notifiable_t *object)
paul@177 214
{
paul@534 215
  /* Acquire the lock for state lookup. */
paul@534 216
paul@534 217
  std::unique_lock<std::mutex> state_guard(_state_lock);
paul@177 218
paul@534 219
  ObjectNotificationState &state = object_state(object, false);
paul@534 220
paul@534 221
  if (state.is_null())
paul@534 222
    return -L4_ENOENT;
paul@177 223
paul@290 224
  /* Unsubscribe via the notification interface. */
paul@288 225
paul@479 226
  client_Notification notify(object->base->ref);
paul@288 227
paul@548 228
  notify.unsubscribe();
paul@483 229
paul@534 230
  return remove_endpoint(object, state.endpoint);
paul@483 231
}
paul@483 232
paul@483 233
/* Remove a notification endpoint for an object. */
paul@483 234
paul@483 235
long ObjectNotifier::remove_endpoint(notifiable_t *object, l4_cap_idx_t endpoint)
paul@483 236
{
paul@483 237
  if (l4_is_invalid_cap(endpoint))
paul@483 238
    return -L4_EINVAL;
paul@483 239
paul@483 240
  ipc_cap_free_um(endpoint);
paul@288 241
paul@479 242
  _state.erase(object);
paul@177 243
paul@479 244
  /* Remove the lock for updating object state. */
paul@177 245
paul@479 246
  _object_locks.erase(object);
paul@177 247
paul@177 248
  return L4_EOK;
paul@177 249
}
paul@177 250
paul@177 251
paul@177 252
paul@537 253
/* Handle a notification event for an object. */
paul@177 254
paul@534 255
void GeneralObjectNotifier::notify(notifiable_t *object, notify_flags_t flags,
paul@534 256
                                   notify_values_t values)
paul@177 257
{
paul@479 258
  /* Enter critical section for the notifier (affecting all objects). */
paul@177 259
paul@177 260
  std::unique_lock<std::mutex> general_guard(_general_lock);
paul@177 261
paul@177 262
  /* Acquire the lock for state lookup. */
paul@177 263
paul@177 264
  std::unique_lock<std::mutex> state_guard(_state_lock);
paul@177 265
paul@479 266
  ObjectNotificationState &state = object_state(object, false);
paul@177 267
paul@177 268
  if (state.is_null())
paul@177 269
    return;
paul@177 270
paul@479 271
  /* Acquire the lock for the object state itself. */
paul@177 272
paul@479 273
  std::unique_lock<std::mutex> object_guard(state.lock);
paul@177 274
paul@481 275
  /* Record flags and note previous flags. */
paul@177 276
paul@481 277
  notify_flags_t recorded = state.pending_notifications;
paul@177 278
paul@481 279
  state.pending_notifications |= flags;
paul@481 280
  state.pending_values = values;
paul@177 281
paul@479 282
  /* Add an object queue entry for any objects without previous notifications. */
paul@177 283
paul@177 284
  if (!recorded)
paul@479 285
    _affected.push_back(object);
paul@177 286
paul@177 287
  /* Notify any waiting caller. */
paul@177 288
paul@177 289
  _general_condition.notify_one();
paul@177 290
}
paul@177 291
paul@534 292
void SpecificObjectNotifier::notify(notifiable_t *object, notify_flags_t flags,
paul@534 293
                                    notify_values_t values)
paul@177 294
{
paul@177 295
  /* Acquire the lock for state lookup. */
paul@177 296
paul@177 297
  std::unique_lock<std::mutex> state_guard(_state_lock);
paul@177 298
paul@479 299
  ObjectNotificationState &state = object_state(object, false);
paul@177 300
paul@177 301
  if (state.is_null())
paul@177 302
    return;
paul@177 303
paul@479 304
  /* Acquire the lock for the object state itself. */
paul@177 305
paul@479 306
  std::unique_lock<std::mutex> object_guard(state.lock);
paul@177 307
paul@481 308
  state.pending_notifications |= flags;
paul@481 309
  state.pending_values = values;
paul@177 310
paul@177 311
  /* Notify any waiting caller. */
paul@177 312
paul@177 313
  state.condition.notify_one();
paul@177 314
}
paul@177 315
paul@177 316
paul@177 317
paul@479 318
/* Transfer pending notifications to the given object. This must be called with
paul@479 319
   a lock acquired on the object notification state. */
paul@177 320
paul@479 321
bool ObjectNotifier::_transfer(ObjectNotificationState &state, notifiable_t *object)
paul@177 322
{
paul@481 323
  notify_flags_t recorded = state.pending_notifications;
paul@177 324
paul@177 325
  if (recorded)
paul@177 326
  {
paul@479 327
    object->notifications = recorded;
paul@481 328
    object->values = state.pending_values;
paul@481 329
    state.pending_notifications = 0;
paul@177 330
    return true;
paul@177 331
  }
paul@177 332
paul@177 333
  return false;
paul@177 334
}
paul@177 335
paul@177 336
paul@177 337
paul@479 338
/* Obtain object state and transfer notifications. */
paul@177 339
paul@479 340
bool GeneralObjectNotifier::_retrieve_for_object(notifiable_t *object)
paul@177 341
{
paul@177 342
  /* Acquire the lock for state lookup. */
paul@177 343
paul@177 344
  std::unique_lock<std::mutex> state_guard(_state_lock);
paul@177 345
paul@479 346
  ObjectNotificationState &state = object_state(object, false);
paul@177 347
paul@177 348
  if (state.is_null())
paul@177 349
    return false;
paul@177 350
paul@479 351
  /* Acquire the lock for the object state itself, then release the state lock. */
paul@177 352
paul@479 353
  std::unique_lock<std::mutex> object_guard(state.lock);
paul@177 354
paul@177 355
  state_guard.unlock();
paul@177 356
paul@177 357
  /* Call generic method to transfer notifications, if possible. */
paul@177 358
paul@479 359
  return _transfer(state, object);
paul@177 360
}
paul@177 361
paul@479 362
/* Obtain queued objects until one is found that still has events recorded for
paul@479 363
   it. This must be called with the notifier's general lock acquired. */
paul@177 364
paul@479 365
bool GeneralObjectNotifier::_retrieve(notifiable_t **object)
paul@177 366
{
paul@177 367
  while (!_affected.empty())
paul@177 368
  {
paul@479 369
    *object = _affected.front();
paul@177 370
    _affected.pop_front();
paul@177 371
paul@479 372
    if (_retrieve_for_object(*object))
paul@177 373
      return true;
paul@177 374
  }
paul@177 375
paul@177 376
  return false;
paul@177 377
}
paul@177 378
paul@177 379
paul@177 380
paul@479 381
/* Wait for notification events on objects. */
paul@124 382
paul@479 383
long GeneralObjectNotifier::wait(notifiable_t **object)
paul@124 384
{
paul@177 385
  std::unique_lock<std::mutex> general_guard(_general_lock);
paul@124 386
paul@124 387
  while (1)
paul@124 388
  {
paul@479 389
    /* With pending notifications, update the first object and exit. */
paul@129 390
paul@479 391
    if (_retrieve(object))
paul@177 392
      break;
paul@129 393
paul@177 394
    /* Otherwise, wait for notifications. */
paul@129 395
paul@177 396
    _general_condition.wait(general_guard);
paul@129 397
  }
paul@129 398
paul@129 399
  return L4_EOK;
paul@129 400
}
paul@129 401
paul@479 402
/* Wait for notifications from a single object. */
paul@129 403
paul@479 404
long SpecificObjectNotifier::wait_object(notifiable_t *object)
paul@129 405
{
paul@479 406
  /* Acquire the lock for reading object state. */
paul@177 407
paul@177 408
  std::unique_lock<std::mutex> state_guard(_state_lock);
paul@177 409
paul@479 410
  ObjectNotificationState &state = object_state(object, false);
paul@177 411
paul@177 412
  if (state.is_null())
paul@177 413
    return -L4_EINVAL;
paul@177 414
paul@479 415
  /* Acquire the lock for the object state itself, then release the state lock. */
paul@177 416
paul@479 417
  std::unique_lock<std::mutex> object_guard(state.lock);
paul@177 418
paul@177 419
  state_guard.unlock();
paul@129 420
paul@129 421
  while (1)
paul@129 422
  {
paul@479 423
    /* With pending notifications, update the object and exit. */
paul@129 424
paul@479 425
    if (_transfer(state, object))
paul@177 426
      break;
paul@129 427
paul@177 428
    /* Otherwise, wait for notifications. */
paul@129 429
paul@479 430
    state.condition.wait(object_guard);
paul@124 431
  }
paul@124 432
paul@124 433
  return L4_EOK;
paul@124 434
}
paul@124 435
paul@534 436
paul@534 437
paul@534 438
/* Object-specific resource methods. */
paul@534 439
paul@534 440
ipc_server_default_config_type NotifierResource::config()
paul@534 441
{
paul@534 442
  return config_Notifier;
paul@534 443
}
paul@534 444
paul@534 445
/* Register a notification received by an object-specific resource. */
paul@534 446
paul@534 447
long NotifierResource::notify(notify_flags_t flags, notify_values_t values)
paul@534 448
{
paul@534 449
  if (_notifier != NULL)
paul@534 450
    _notifier->notify(_object, flags, values);
paul@534 451
paul@534 452
  return L4_EOK;
paul@534 453
}
paul@534 454
paul@124 455
// vim: tabstop=2 expandtab shiftwidth=2