1 
2 #include "config.h"
3 
4 #include "event.h"
5 
6 #include <algorithm>
7 #include <atomic>
8 #include <cstring>
9 #include <exception>
10 #include <memory>
11 #include <mutex>
12 #include <new>
13 #include <string>
14 #include <thread>
15 #include <utility>
16 
17 #include "AL/al.h"
18 #include "AL/alc.h"
19 
20 #include "albyte.h"
21 #include "alcontext.h"
22 #include "almalloc.h"
23 #include "async_event.h"
24 #include "core/except.h"
25 #include "core/logging.h"
26 #include "effects/base.h"
27 #include "inprogext.h"
28 #include "opthelpers.h"
29 #include "ringbuffer.h"
30 #include "threads.h"
31 #include "voice_change.h"
32 
33 
EventThread(ALCcontext * context)34 static int EventThread(ALCcontext *context)
35 {
36     RingBuffer *ring{context->mAsyncEvents.get()};
37     bool quitnow{false};
38     while LIKELY(!quitnow)
39     {
40         auto evt_data = ring->getReadVector().first;
41         if(evt_data.len == 0)
42         {
43             context->mEventSem.wait();
44             continue;
45         }
46 
47         std::lock_guard<std::mutex> _{context->mEventCbLock};
48         do {
49             auto *evt_ptr = reinterpret_cast<AsyncEvent*>(evt_data.buf);
50             evt_data.buf += sizeof(AsyncEvent);
51             evt_data.len -= 1;
52 
53             AsyncEvent evt{*evt_ptr};
54             al::destroy_at(evt_ptr);
55             ring->readAdvance(1);
56 
57             quitnow = evt.EnumType == EventType_KillThread;
58             if UNLIKELY(quitnow) break;
59 
60             if(evt.EnumType == EventType_ReleaseEffectState)
61             {
62                 evt.u.mEffectState->release();
63                 continue;
64             }
65 
66             uint enabledevts{context->mEnabledEvts.load(std::memory_order_acquire)};
67             if(!context->mEventCb) continue;
68 
69             if(evt.EnumType == EventType_SourceStateChange)
70             {
71                 if(!(enabledevts&EventType_SourceStateChange))
72                     continue;
73                 ALuint state{};
74                 std::string msg{"Source ID " + std::to_string(evt.u.srcstate.id)};
75                 msg += " state has changed to ";
76                 switch(evt.u.srcstate.state)
77                 {
78                 case VChangeState::Reset:
79                     msg += "AL_INITIAL";
80                     state = AL_INITIAL;
81                     break;
82                 case VChangeState::Stop:
83                     msg += "AL_STOPPED";
84                     state = AL_STOPPED;
85                     break;
86                 case VChangeState::Play:
87                     msg += "AL_PLAYING";
88                     state = AL_PLAYING;
89                     break;
90                 case VChangeState::Pause:
91                     msg += "AL_PAUSED";
92                     state = AL_PAUSED;
93                     break;
94                 /* Shouldn't happen */
95                 case VChangeState::Restart:
96                     break;
97                 }
98                 context->mEventCb(AL_EVENT_TYPE_SOURCE_STATE_CHANGED_SOFT, evt.u.srcstate.id,
99                     state, static_cast<ALsizei>(msg.length()), msg.c_str(), context->mEventParam);
100             }
101             else if(evt.EnumType == EventType_BufferCompleted)
102             {
103                 if(!(enabledevts&EventType_BufferCompleted))
104                     continue;
105                 std::string msg{std::to_string(evt.u.bufcomp.count)};
106                 if(evt.u.bufcomp.count == 1) msg += " buffer completed";
107                 else msg += " buffers completed";
108                 context->mEventCb(AL_EVENT_TYPE_BUFFER_COMPLETED_SOFT, evt.u.bufcomp.id,
109                     evt.u.bufcomp.count, static_cast<ALsizei>(msg.length()), msg.c_str(),
110                     context->mEventParam);
111             }
112             else if(evt.EnumType == EventType_Disconnected)
113             {
114                 if(!(enabledevts&EventType_Disconnected))
115                     continue;
116                 context->mEventCb(AL_EVENT_TYPE_DISCONNECTED_SOFT, 0, 0,
117                     static_cast<ALsizei>(strlen(evt.u.disconnect.msg)), evt.u.disconnect.msg,
118                     context->mEventParam);
119             }
120         } while(evt_data.len != 0);
121     }
122     return 0;
123 }
124 
StartEventThrd(ALCcontext * ctx)125 void StartEventThrd(ALCcontext *ctx)
126 {
127     try {
128         ctx->mEventThread = std::thread{EventThread, ctx};
129     }
130     catch(std::exception& e) {
131         ERR("Failed to start event thread: %s\n", e.what());
132     }
133     catch(...) {
134         ERR("Failed to start event thread! Expect problems.\n");
135     }
136 }
137 
StopEventThrd(ALCcontext * ctx)138 void StopEventThrd(ALCcontext *ctx)
139 {
140     RingBuffer *ring{ctx->mAsyncEvents.get()};
141     auto evt_data = ring->getWriteVector().first;
142     if(evt_data.len == 0)
143     {
144         do {
145             std::this_thread::yield();
146             evt_data = ring->getWriteVector().first;
147         } while(evt_data.len == 0);
148     }
149     ::new(evt_data.buf) AsyncEvent{EventType_KillThread};
150     ring->writeAdvance(1);
151 
152     ctx->mEventSem.post();
153     if(ctx->mEventThread.joinable())
154         ctx->mEventThread.join();
155 }
156 
alEventControlSOFT(ALsizei count,const ALenum * types,ALboolean enable)157 AL_API void AL_APIENTRY alEventControlSOFT(ALsizei count, const ALenum *types, ALboolean enable)
158 START_API_FUNC
159 {
160     ContextRef context{GetContextRef()};
161     if UNLIKELY(!context) return;
162 
163     if(count < 0) context->setError(AL_INVALID_VALUE, "Controlling %d events", count);
164     if(count <= 0) return;
165     if(!types) SETERR_RETURN(context, AL_INVALID_VALUE,, "NULL pointer");
166 
167     uint flags{0};
168     const ALenum *types_end = types+count;
169     auto bad_type = std::find_if_not(types, types_end,
170         [&flags](ALenum type) noexcept -> bool
171         {
172             if(type == AL_EVENT_TYPE_BUFFER_COMPLETED_SOFT)
173                 flags |= EventType_BufferCompleted;
174             else if(type == AL_EVENT_TYPE_SOURCE_STATE_CHANGED_SOFT)
175                 flags |= EventType_SourceStateChange;
176             else if(type == AL_EVENT_TYPE_DISCONNECTED_SOFT)
177                 flags |= EventType_Disconnected;
178             else
179                 return false;
180             return true;
181         }
182     );
183     if(bad_type != types_end)
184         SETERR_RETURN(context, AL_INVALID_ENUM,, "Invalid event type 0x%04x", *bad_type);
185 
186     if(enable)
187     {
188         uint enabledevts{context->mEnabledEvts.load(std::memory_order_relaxed)};
189         while(context->mEnabledEvts.compare_exchange_weak(enabledevts, enabledevts|flags,
190             std::memory_order_acq_rel, std::memory_order_acquire) == 0)
191         {
192             /* enabledevts is (re-)filled with the current value on failure, so
193              * just try again.
194              */
195         }
196     }
197     else
198     {
199         uint enabledevts{context->mEnabledEvts.load(std::memory_order_relaxed)};
200         while(context->mEnabledEvts.compare_exchange_weak(enabledevts, enabledevts&~flags,
201             std::memory_order_acq_rel, std::memory_order_acquire) == 0)
202         {
203         }
204         /* Wait to ensure the event handler sees the changed flags before
205          * returning.
206          */
207         std::lock_guard<std::mutex>{context->mEventCbLock};
208     }
209 }
210 END_API_FUNC
211 
alEventCallbackSOFT(ALEVENTPROCSOFT callback,void * userParam)212 AL_API void AL_APIENTRY alEventCallbackSOFT(ALEVENTPROCSOFT callback, void *userParam)
213 START_API_FUNC
214 {
215     ContextRef context{GetContextRef()};
216     if UNLIKELY(!context) return;
217 
218     std::lock_guard<std::mutex> _{context->mPropLock};
219     std::lock_guard<std::mutex> __{context->mEventCbLock};
220     context->mEventCb = callback;
221     context->mEventParam = userParam;
222 }
223 END_API_FUNC
224