1 /* 2 * Copyright (C) 2023 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17 #ifndef BERBERIS_RUNTIME_PRIMITIVES_SIGNAL_QUEUE_H_ 18 #define BERBERIS_RUNTIME_PRIMITIVES_SIGNAL_QUEUE_H_ 19 20 #include <csignal> 21 22 #include <atomic> 23 24 #include "berberis/base/macros.h" 25 26 namespace berberis { 27 28 // Enqueue signals from multiple threads and signal handlers. 29 // Dequeue signals from target thread only. 30 // Signals with smaller numbers have higher priority. 31 // Signals with equal numbers are FIFO. 32 // 33 // ATTENTION: It's somewhat subtle that we need priorities here. 34 // As we sit on top of host signals, they are already delivered by priorities, 35 // thus we might use non-priority queue... But this is not completely true! 36 // The issue is that we don't run signal handlers immediately when signals are 37 // delivered. If a signal handler raises another signal with high priority, it 38 // must be delivered before already-queued signals with lower priorities. 39 // 40 // In fact this is multi producer, single consumer lock-free queue. 41 // Enqueue by pushing to shared lock-free single-linked 'produced' list. 42 // Dequeue by moving everything from 'produced' to non-shared 'consumed' list 43 // and then doing linear search by priority. As expected count of pending 44 // signals is small, this should have acceptable performance. 45 // No ABA as there is only one consumer. 46 class SignalQueue { 47 public: SignalQueue()48 constexpr SignalQueue() : produced_(nullptr), consumed_(nullptr) {} 49 50 // Allocate signal. 51 siginfo_t* AllocSignal(); 52 53 // Add allocated signal to the queue. 54 // Can be called from signal handlers. 55 // Can be called concurrently from multiple threads. 56 void EnqueueSignal(siginfo_t* info); 57 58 // Get next signal from the queue according to priorities. 59 // ATTENTION: call from single thread only! 60 siginfo_t* DequeueSignalUnsafe(); 61 62 // Free dequeued signal. 63 void FreeSignal(siginfo_t* info); 64 65 private: 66 // Can reinterpret_cast siginfo_t* -> Node*! 67 struct Node { 68 siginfo_t info; 69 Node* next; 70 }; 71 72 std::atomic<Node*> produced_; 73 Node* consumed_; 74 75 DISALLOW_COPY_AND_ASSIGN(SignalQueue); 76 }; 77 78 } // namespace berberis 79 80 #endif // BERBERIS_RUNTIME_PRIMITIVES_SIGNAL_QUEUE_H_ 81