1 /* 2 * Copyright 2014, Paweł Dziepak, pdziepak@quarnos.org. 3 * Copyright 2011, Ingo Weinhold, ingo_weinhold@gmx.de. 4 * Distributed under the terms of the MIT License. 5 */ 6 7 8 #include <UserEvent.h> 9 10 #include <ksignal.h> 11 #include <thread_types.h> 12 #include <util/AutoLock.h> 13 14 15 // #pragma mark - UserEvent 16 17 18 UserEvent::~UserEvent() 19 { 20 } 21 22 23 // #pragma mark - SignalEvent 24 25 26 struct SignalEvent::EventSignal : Signal { 27 EventSignal(uint32 number, int32 signalCode, int32 errorCode, 28 pid_t sendingProcess) 29 : 30 Signal(number, signalCode, errorCode, sendingProcess), 31 fInUse(0) 32 { 33 } 34 35 bool MarkUsed() 36 { 37 return atomic_get_and_set(&fInUse, 1) != 0; 38 } 39 40 void SetUnused() 41 { 42 // mark not-in-use 43 atomic_set(&fInUse, 0); 44 } 45 46 virtual void Handled() 47 { 48 SetUnused(); 49 50 Signal::Handled(); 51 } 52 53 private: 54 int32 fInUse; 55 }; 56 57 58 SignalEvent::SignalEvent(EventSignal* signal) 59 : 60 fSignal(signal), 61 fPendingDPC(0) 62 { 63 } 64 65 66 SignalEvent::~SignalEvent() 67 { 68 fSignal->ReleaseReference(); 69 } 70 71 72 void 73 SignalEvent::SetUserValue(union sigval userValue) 74 { 75 fSignal->SetUserValue(userValue); 76 } 77 78 79 status_t 80 SignalEvent::Fire() 81 { 82 bool wasPending = atomic_get_and_set(&fPendingDPC, 1) != 0; 83 if (wasPending) 84 return B_BUSY; 85 86 if (fSignal->MarkUsed()) { 87 atomic_set(&fPendingDPC, 0); 88 return B_BUSY; 89 } 90 91 AcquireReference(); 92 DPCQueue::DefaultQueue(B_NORMAL_PRIORITY)->Add(this); 93 94 return B_OK; 95 } 96 97 98 // #pragma mark - TeamSignalEvent 99 100 101 TeamSignalEvent::TeamSignalEvent(Team* team, EventSignal* signal) 102 : 103 SignalEvent(signal), 104 fTeam(team) 105 { 106 } 107 108 109 /*static*/ TeamSignalEvent* 110 TeamSignalEvent::Create(Team* team, uint32 signalNumber, int32 signalCode, 111 int32 errorCode) 112 { 113 // create the signal 114 EventSignal* signal = new(std::nothrow) EventSignal(signalNumber, 115 signalCode, errorCode, team->id); 116 if (signal == NULL) 117 return NULL; 118 119 // create the event 120 TeamSignalEvent* event = new(std::nothrow) TeamSignalEvent(team, signal); 121 if (event == NULL) { 122 delete signal; 123 return NULL; 124 } 125 126 return event; 127 } 128 129 130 status_t 131 TeamSignalEvent::Fire() 132 { 133 // We need a reference to the team to guarantee that it is still there when 134 // the DPC actually runs. 135 fTeam->AcquireReference(); 136 status_t result = SignalEvent::Fire(); 137 if (result != B_OK) 138 fTeam->ReleaseReference(); 139 140 return result; 141 } 142 143 144 void 145 TeamSignalEvent::DoDPC(DPCQueue* queue) 146 { 147 fSignal->AcquireReference(); 148 // one reference is transferred to send_signal_to_team_locked 149 150 InterruptsSpinLocker locker(fTeam->signal_lock); 151 status_t error = send_signal_to_team_locked(fTeam, fSignal->Number(), 152 fSignal, B_DO_NOT_RESCHEDULE); 153 locker.Unlock(); 154 fTeam->ReleaseReference(); 155 156 // There are situations (for certain signals), in which 157 // send_signal_to_team_locked() succeeds without queuing the signal. 158 if (error != B_OK || !fSignal->IsPending()) 159 fSignal->SetUnused(); 160 161 // We're no longer queued in the DPC queue, so we can be reused. 162 atomic_set(&fPendingDPC, 0); 163 164 ReleaseReference(); 165 } 166 167 168 // #pragma mark - ThreadSignalEvent 169 170 171 ThreadSignalEvent::ThreadSignalEvent(Thread* thread, EventSignal* signal) 172 : 173 SignalEvent(signal), 174 fThread(thread) 175 { 176 } 177 178 179 /*static*/ ThreadSignalEvent* 180 ThreadSignalEvent::Create(Thread* thread, uint32 signalNumber, int32 signalCode, 181 int32 errorCode, pid_t sendingTeam) 182 { 183 // create the signal 184 EventSignal* signal = new(std::nothrow) EventSignal(signalNumber, 185 signalCode, errorCode, sendingTeam); 186 if (signal == NULL) 187 return NULL; 188 189 // create the event 190 ThreadSignalEvent* event = new(std::nothrow) ThreadSignalEvent(thread, signal); 191 if (event == NULL) { 192 delete signal; 193 return NULL; 194 } 195 196 return event; 197 } 198 199 200 status_t 201 ThreadSignalEvent::Fire() 202 { 203 // We need a reference to the thread to guarantee that it is still there 204 // when the DPC actually runs. 205 fThread->AcquireReference(); 206 status_t result = SignalEvent::Fire(); 207 if (result != B_OK) 208 fThread->ReleaseReference(); 209 210 return result; 211 } 212 213 214 void 215 ThreadSignalEvent::DoDPC(DPCQueue* queue) 216 { 217 fSignal->AcquireReference(); 218 // one reference is transferred to send_signal_to_team_locked 219 InterruptsReadSpinLocker teamLocker(fThread->team_lock); 220 SpinLocker locker(fThread->team->signal_lock); 221 status_t error = send_signal_to_thread_locked(fThread, fSignal->Number(), 222 fSignal, B_DO_NOT_RESCHEDULE); 223 locker.Unlock(); 224 teamLocker.Unlock(); 225 fThread->ReleaseReference(); 226 227 // There are situations (for certain signals), in which 228 // send_signal_to_team_locked() succeeds without queuing the signal. 229 if (error != B_OK || !fSignal->IsPending()) 230 fSignal->SetUnused(); 231 232 // We're no longer queued in the DPC queue, so we can be reused. 233 atomic_set(&fPendingDPC, 0); 234 235 ReleaseReference(); 236 } 237 238 239 // #pragma mark - UserEvent 240 241 242 CreateThreadEvent::CreateThreadEvent(const ThreadCreationAttributes& attributes) 243 : 244 fCreationAttributes(attributes), 245 fPendingDPC(0) 246 { 247 // attributes.name is a pointer to a temporary buffer. Copy the name into 248 // our own buffer and replace the name pointer. 249 strlcpy(fThreadName, attributes.name, sizeof(fThreadName)); 250 fCreationAttributes.name = fThreadName; 251 } 252 253 254 /*static*/ CreateThreadEvent* 255 CreateThreadEvent::Create(const ThreadCreationAttributes& attributes) 256 { 257 return new(std::nothrow) CreateThreadEvent(attributes); 258 } 259 260 261 status_t 262 CreateThreadEvent::Fire() 263 { 264 bool wasPending = atomic_get_and_set(&fPendingDPC, 1) != 0; 265 if (wasPending) 266 return B_BUSY; 267 268 AcquireReference(); 269 DPCQueue::DefaultQueue(B_NORMAL_PRIORITY)->Add(this); 270 271 return B_OK; 272 } 273 274 275 void 276 CreateThreadEvent::DoDPC(DPCQueue* queue) 277 { 278 // We're no longer queued in the DPC queue, so we can be reused. 279 atomic_set(&fPendingDPC, 0); 280 281 // create the thread 282 thread_id threadID = thread_create_thread(fCreationAttributes, false); 283 if (threadID >= 0) 284 resume_thread(threadID); 285 286 ReleaseReference(); 287 } 288