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