1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264
|
/** Declaration of DKEndpointManager class that manages D-Bus endpoints.
Copyright (C) 2011 Free Software Foundation, Inc.
Written by: Niels Grewe <niels.grewe@halbordnung.de>
Created: January 2011
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public
License as published by the Free Software Foundation; either
version 2 of the License, or (at your option) any later version.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Library General Public License for more details.
You should have received a copy of the GNU Lesser General Public
License along with this library; if not, write to the Free
Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
Boston, MA 02111 USA.
*/
#import <Foundation/NSObject.h>
#include <stdint.h>
#include <dbus/dbus.h>
@class DKEndpoint, DKProxy, NSLock, NSMapTable, NSThread, NSRecursiveLock;
// The structure members should all be of the same size, so the compiler should
// not do any awkward packing.
typedef struct {
id target;
SEL selector;
id object;
intptr_t* returnPointer;
} DKRingBufferElement;
/**
* DKEndpointManager is a singleton class that maintains a thread to interact
* with D-Bus. It is responsible for creating and tracking the endpoints to
* the specific busses and will attempt to recover from connection failures.
*
* DKEndpointManager also provides a synchronized mode so that it can be safely
* called from +initialize methods. In that case, the caller is expected to wrap
* method calls that might trigger the manager (especially to DKEndpoint,
* DKPort, DKPortNameServer, DKProxy, or DKNotificationCenter) with calls to
* -enterInitialize and -leaveInitialize.
*/
@interface DKEndpointManager: NSObject
{
/**
* The thread running the runloop which interacts with libdbus.
*/
NSThread *workerThread;
@private
/**
* Tracks whether the thread has been started.
*/
BOOL threadStarted;
/**
* Tracks whether we already enabled threading.
*/
BOOL threadEnabled;
/**
* Maps active DBusConnections to the corresponding DKEndpoints.
*/
NSMapTable *activeConnections;
/**
* Lock to protect changes to the connection tables.
*/
NSRecursiveLock *connectionStateLock;
/**
* A (oneway) ring buffer for queuing tuples of the following form:
* <target, selector, data, pointer-to-return>. The tuples are inserted
* whenever a libdbus callback requires a value to be returned from the worker
* thread.
*/
DKRingBufferElement *ringBuffer;
/**
* Free-running counter for the producer threads
*/
uint32_t producerCounter;
/**
* Since it is possible for more than one thread to write to the ring buffer,
* it cannot be completely lockless: Producers need to obtain the
* <ivar>producerLock</ivar> in order to prevent overwriting. Since there is
* only one consumer thread, it needs no such provisions.
*/
NSLock *producerLock;
/**
* Free-running counter for the consumer thread
*/
uint32_t consumerCounter;
/**
* Counter to track how many callers are calling into the endpoint-manager
* from +initialize.
*/
NSUInteger initializeRefCount;
/**
* Lock to protect changes to the accounting tables in synchronised mode.
*/
NSRecursiveLock *synchronizationStateLock;
/**
* The <ivar>syncedWatchers</ivar> map table keeps track of watchers that were
* created while the endpoint manager is in synchronised mode. Each is mapped
* to the thread it was created in. When the last +initialize call using the
* manager finishes, the manager will reap these watchers and reschedule them
* on the worker thread.
*/
NSMapTable *syncedWatchers;
/**
* The <ivar>syncedTimers</ivar> map table keeps track of watchers that were
* created while the endpoint manager is in synchronised mode. Each is mapped
* to the thread it was created in. When the last +initialize call using the
* manager finishes, the manager will invalidate these timers and reschedule
* then on the worker thread.
*/
NSMapTable *syncedTimers;
}
/**
* Returns the shared endpoint manager that is used to manage interactions with
* libdbus.
*/
+ (id)sharedEndpointManager;
/**
* Returns a reference to the worker thread that interacts with D-Bus.
*/
- (NSThread*)workerThread;
/**
* Creates or reuses an endpoint.
*/
- (id)endpointForDBusConnection: (DBusConnection*)connection
mergingInfo: (NSDictionary*)info;
/**
* Returns an endpoint connected to an arbitrary address. This is only useful
* for specific cases where you don't want to use one of the standard message
* busses. Use -endpointForWellKnownBus: to get a connection for one of those.
*/
- (DKEndpoint*)endpointForConnectionTo: (NSString*)address;
/**
* Returns an endpoint connected to one of the well-known message busses as per
* D-Bus documentation (i.e. DBUS_BUS_SYSTEM, DBUS_BUS_SESSION or
* DBUS_BUS_STARTER).
*/
- (DKEndpoint*)endpointForWellKnownBus: (DBusBusType)type;
/**
* Method to be called by endpoints that are being deallocated.
*/
- (void)removeEndpointForDBusConnection: (DBusConnection*)connection;
/**
* Entry point for the worker thread.
*/
- (void)start: (id)ignored;
/**
* Schedules periodic recovery attempts for <var>endpoint/var>. Will be used in
* case of bus failures. If recovery is successful, <var>aProxy</var> will be
* notified.
*/
- (void)attemptRecoveryForEndpoint: (DKEndpoint*)endpoint
proxy: (DKProxy*)aProxy;
/**
* Inserts the request into the ring buffer and schedules it for draining in the
* worker thread. With <var>doWait</var> set to YES this method becomes a
* synchonisation point: It will spin until the request has completed. This
* should only be used when a return value is required by the libdbus API.
*/
- (BOOL)boolReturnForPerformingSelector: (SEL)selector
target: (id)target
data: (void*)data
waitForReturn: (BOOL)doWait;
/**
* Called from within the worker thread to process requests from the ring
* buffer.
*/
- (void)drainBuffer: (id)ignored;
/**
* Will be called in order to enable threaded mode.
*/
- (void)enableThread;
/**
* Will be called by DBusKit classes that require usage of the bus in their
* +initialize method.
*/
- (void)enterInitialize;
/**
* Will be called by DBusKit classes that require usage of the bus in their
* +initialize method.
*/
- (void)leaveInitialize;
/**
* This method can be used to determine whether the manager is in synchronized
* mode due to being called from an initialize method.
*/
- (BOOL)isSynchronizing;
/**
* This method will be used by instances of <class>DKRunLoopContext</class> to
* inform the endpoint manager of timers it is presently using. If the manager
* is in synchronized mode (i.e. being called from +initialize), a reference to
* the timer will be tracked until it either no longer needed or has
* successfully been rescheduled on the worker thread. In order to track all
* data required, the <var>context</var> the timer comes from must be specified.
*/
- (void)registerTimer: (id)timer
fromContext: (id)context;
/**
* This method will be used by instances of <class>DKRunLoopContext</class> to
* inform the endpoint manager of <class>DKWatcher</class> instances it is
* presently using to monitor file descriptors on behalf of libdbus. If the manager
* is in synchronized mode (i.e. being called from +initialize), a reference to
* the watcher will be tracked until it either no longer needed or has
* successfully been rescheduled on the worker thread.
*/
- (void)registerWatcher: (id)watcher;
/**
* If the receiver is in synchronized mode, this removes the reference to the
* timer object.
*/
- (void)unregisterTimer: (id)timer;
/**
* If the receiver is in synchronized mode, this removes the reference to the
* watcher object.
*/
- (void)unregisterWatcher: (id)watcher;
@end
/**
* Macro to check whether the code is presently executing in the worker thread
*/
#define DKInWorkerThread (BOOL)[[[DKEndpointManager sharedEndpointManager] workerThread] isEqual: [NSThread currentThread]];
|