Handle EventType_BufferCompleted uniquely
[openal-soft.git] / OpenAL32 / event.cpp
blob1a4e1df09e47ff148a8bffc37e9108ab25a9da15
2 #include "config.h"
4 #include <algorithm>
6 #include "AL/alc.h"
7 #include "AL/al.h"
8 #include "AL/alext.h"
10 #include "alMain.h"
11 #include "alcontext.h"
12 #include "alError.h"
13 #include "alAuxEffectSlot.h"
14 #include "ringbuffer.h"
15 #include "threads.h"
18 static int EventThread(ALCcontext *context)
20 bool quitnow{false};
21 while(LIKELY(!quitnow))
23 AsyncEvent evt;
24 if(ll_ringbuffer_read(context->AsyncEvents, &evt, 1) == 0)
26 context->EventSem.wait();
27 continue;
30 std::lock_guard<std::mutex> _{context->EventCbLock};
31 do {
32 quitnow = evt.EnumType == EventType_KillThread;
33 if(UNLIKELY(quitnow)) break;
35 if(evt.EnumType == EventType_ReleaseEffectState)
37 evt.u.mEffectState->DecRef();
38 continue;
41 ALbitfieldSOFT enabledevts{context->EnabledEvts.load(std::memory_order_acquire)};
42 if(!context->EventCb) continue;
44 if(evt.EnumType == EventType_SourceStateChange)
46 if(!(enabledevts&EventType_SourceStateChange))
47 continue;
48 std::string msg{"Source ID " + std::to_string(evt.u.srcstate.id)};
49 msg += " state has changed to ";
50 msg += (evt.u.srcstate.state==AL_INITIAL) ? "AL_INITIAL" :
51 (evt.u.srcstate.state==AL_PLAYING) ? "AL_PLAYING" :
52 (evt.u.srcstate.state==AL_PAUSED) ? "AL_PAUSED" :
53 (evt.u.srcstate.state==AL_STOPPED) ? "AL_STOPPED" : "<unknown>";
54 context->EventCb(AL_EVENT_TYPE_SOURCE_STATE_CHANGED_SOFT, evt.u.srcstate.id,
55 evt.u.srcstate.state, msg.length(), msg.c_str(), context->EventParam
58 else if(evt.EnumType == EventType_BufferCompleted)
60 if(!(enabledevts&EventType_BufferCompleted))
61 continue;
62 std::string msg{std::to_string(evt.u.bufcomp.count)};
63 if(evt.u.bufcomp.count == 1) msg += " buffer completed";
64 else msg += " buffers completed";
65 context->EventCb(AL_EVENT_TYPE_BUFFER_COMPLETED_SOFT, evt.u.bufcomp.id,
66 evt.u.bufcomp.count, msg.length(), msg.c_str(), context->EventParam
69 else if((enabledevts&evt.EnumType) == evt.EnumType)
70 context->EventCb(evt.u.user.type, evt.u.user.id, evt.u.user.param,
71 (ALsizei)strlen(evt.u.user.msg), evt.u.user.msg, context->EventParam
73 } while(ll_ringbuffer_read(context->AsyncEvents, &evt, 1) != 0);
75 return 0;
78 void StartEventThrd(ALCcontext *ctx)
80 try {
81 ctx->EventThread = std::thread(EventThread, ctx);
83 catch(std::exception& e) {
84 ERR("Failed to start event thread: %s\n", e.what());
86 catch(...) {
87 ERR("Failed to start event thread! Expect problems.\n");
91 void StopEventThrd(ALCcontext *ctx)
93 static constexpr AsyncEvent kill_evt = ASYNC_EVENT(EventType_KillThread);
94 while(ll_ringbuffer_write(ctx->AsyncEvents, &kill_evt, 1) == 0)
95 std::this_thread::yield();
96 ctx->EventSem.post();
97 if(ctx->EventThread.joinable())
98 ctx->EventThread.join();
101 AL_API void AL_APIENTRY alEventControlSOFT(ALsizei count, const ALenum *types, ALboolean enable)
103 ContextRef context{GetContextRef()};
104 if(UNLIKELY(!context)) return;
106 if(count < 0) SETERR_RETURN(context.get(), AL_INVALID_VALUE,, "Controlling %d events", count);
107 if(count == 0) return;
108 if(!types) SETERR_RETURN(context.get(), AL_INVALID_VALUE,, "NULL pointer");
110 ALbitfieldSOFT flags{0};
111 const ALenum *types_end = types+count;
112 auto bad_type = std::find_if_not(types, types_end,
113 [&flags](ALenum type) noexcept -> bool
115 if(type == AL_EVENT_TYPE_BUFFER_COMPLETED_SOFT)
116 flags |= EventType_BufferCompleted;
117 else if(type == AL_EVENT_TYPE_SOURCE_STATE_CHANGED_SOFT)
118 flags |= EventType_SourceStateChange;
119 else if(type == AL_EVENT_TYPE_ERROR_SOFT)
120 flags |= EventType_Error;
121 else if(type == AL_EVENT_TYPE_PERFORMANCE_SOFT)
122 flags |= EventType_Performance;
123 else if(type == AL_EVENT_TYPE_DEPRECATED_SOFT)
124 flags |= EventType_Deprecated;
125 else if(type == AL_EVENT_TYPE_DISCONNECTED_SOFT)
126 flags |= EventType_Disconnected;
127 else
128 return false;
129 return true;
132 if(bad_type != types_end)
133 SETERR_RETURN(context.get(), AL_INVALID_ENUM,, "Invalid event type 0x%04x", *bad_type);
135 if(enable)
137 ALbitfieldSOFT enabledevts{context->EnabledEvts.load(std::memory_order_relaxed)};
138 while(context->EnabledEvts.compare_exchange_weak(enabledevts, enabledevts|flags,
139 std::memory_order_acq_rel, std::memory_order_acquire) == 0)
141 /* enabledevts is (re-)filled with the current value on failure, so
142 * just try again.
146 else
148 ALbitfieldSOFT enabledevts{context->EnabledEvts.load(std::memory_order_relaxed)};
149 while(context->EnabledEvts.compare_exchange_weak(enabledevts, enabledevts&~flags,
150 std::memory_order_acq_rel, std::memory_order_acquire) == 0)
153 /* Wait to ensure the event handler sees the changed flags before
154 * returning.
156 std::lock_guard<std::mutex>{context->EventCbLock};
160 AL_API void AL_APIENTRY alEventCallbackSOFT(ALEVENTPROCSOFT callback, void *userParam)
162 ContextRef context{GetContextRef()};
163 if(UNLIKELY(!context)) return;
165 std::lock_guard<std::mutex> _{context->PropLock};
166 std::lock_guard<std::mutex> __{context->EventCbLock};
167 context->EventCb = callback;
168 context->EventParam = userParam;