1 /* 2 * Notification support. 3 * 4 * Copyright (C) 2021, 2022 Paul Boddie <paul@boddie.org.uk> 5 * 6 * This program is free software; you can redistribute it and/or 7 * modify it under the terms of the GNU General Public License as 8 * published by the Free Software Foundation; either version 2 of 9 * the License, or (at your option) any later version. 10 * 11 * This program is distributed in the hope that it will be useful, 12 * but WITHOUT ANY WARRANTY; without even the implied warranty of 13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 * GNU General Public License for more details. 15 * 16 * You should have received a copy of the GNU General Public License 17 * along with this program; if not, write to the Free Software 18 * Foundation, Inc., 51 Franklin Street, Fifth Floor, 19 * Boston, MA 02110-1301, USA 20 */ 21 22 #include <ipc/cap_alloc.h> 23 24 #include "notification.h" 25 #include "notifier_client.h" 26 27 28 29 /* Initialise endpoints and flags for notifications. */ 30 31 NotificationSupport::NotificationSupport(unsigned int endpoints) 32 : _min_endpoints(endpoints) 33 { 34 _endpoints.resize(_min_endpoints); 35 } 36 37 NotificationSupport::~NotificationSupport() 38 { 39 } 40 41 /* Subscribe to notifications using a notification object, returning the 42 endpoint number. */ 43 44 unsigned int NotificationSupport::subscribe(l4_cap_idx_t notifier, notify_flags_t flags) 45 { 46 std::lock_guard<std::mutex> guard(_lock); 47 48 unsigned int endpoint = _endpoints.size(); 49 50 _endpoints.resize(endpoint + 1); 51 52 /* Propagate deferred flags for new endpoints. */ 53 54 if (_deferred) 55 _endpoints[endpoint].deferred = _deferred; 56 57 _subscribe(endpoint, notifier, flags); 58 59 return endpoint; 60 } 61 62 /* Subscribe to a specific endpoint's notifications using a notification 63 object. */ 64 65 void NotificationSupport::subscribe(unsigned int endpoint, l4_cap_idx_t notifier, notify_flags_t flags) 66 { 67 std::lock_guard<std::mutex> guard(_lock); 68 69 if (endpoint >= _endpoints.size()) 70 { 71 _endpoints.resize(endpoint + 1); 72 73 /* Propagate deferred flags for new endpoints. */ 74 75 if (_deferred) 76 _endpoints[endpoint].deferred = _deferred; 77 } 78 79 _subscribe(endpoint, notifier, flags); 80 } 81 82 /* Common subscription functionality. */ 83 84 void NotificationSupport::_subscribe(unsigned int endpoint, l4_cap_idx_t notifier, notify_flags_t flags) 85 { 86 NotificationEndpoint &ep = _endpoints[endpoint]; 87 88 ep.notifiers.insert(notifier); 89 ep.flags = flags; 90 91 /* Send deferred conditions held from before subscription occurred. */ 92 93 if (ep.deferred) 94 { 95 _notify(endpoint, ep.deferred); 96 ep.deferred = 0; 97 } 98 } 99 100 /* Notify a particular endpoint. */ 101 102 void NotificationSupport::notify(unsigned int endpoint, notify_flags_t flags) 103 { 104 std::lock_guard<std::mutex> guard(_lock); 105 106 _notify(endpoint, flags); 107 } 108 109 void NotificationSupport::_notify(unsigned int endpoint, notify_flags_t flags) 110 { 111 if (endpoint >= _endpoints.size()) 112 return; 113 114 NotificationEndpoint &ep = _endpoints[endpoint]; 115 116 /* Notify the endpoint or hold any notification for potential future 117 subscription. */ 118 119 if (!ep.notifiers.empty()) 120 { 121 if (flags & ep.flags) 122 { 123 NotifierSet::iterator it; 124 125 for (it = ep.notifiers.begin(); it != ep.notifiers.end(); it++) 126 { 127 client_Notifier notifier(*it); 128 129 notifier.notify(flags & ep.flags); 130 } 131 } 132 } 133 else 134 ep.deferred = flags; 135 } 136 137 /* Notify all endpoints. */ 138 139 void NotificationSupport::notify_all(notify_flags_t flags) 140 { 141 std::lock_guard<std::mutex> guard(_lock); 142 143 for (unsigned int i = 0; i < _endpoints.size(); i++) 144 _notify(i, flags); 145 146 _deferred |= flags; 147 } 148 149 /* Notify the other endpoints. */ 150 151 void NotificationSupport::notify_others(unsigned int endpoint, notify_flags_t flags) 152 { 153 std::lock_guard<std::mutex> guard(_lock); 154 155 for (unsigned int i = 0; i < _endpoints.size(); i++) 156 if (i != endpoint) 157 _notify(i, flags); 158 159 _deferred |= flags; 160 } 161 162 /* Release notifiers for each endpoint. */ 163 164 void NotificationSupport::release_notifiers() 165 { 166 std::lock_guard<std::mutex> guard(_lock); 167 168 for (unsigned int endpoint = 0; endpoint < _endpoints.size(); endpoint++) 169 { 170 NotificationEndpoint &ep = _endpoints[endpoint]; 171 NotifierSet::iterator it; 172 173 for (it = ep.notifiers.begin(); it != ep.notifiers.end(); it++) 174 ipc_cap_free_um(*it); 175 176 ep.notifiers.clear(); 177 ep.flags = 0; 178 ep.deferred = 0; 179 } 180 } 181 182 // vim: tabstop=4 expandtab shiftwidth=4