22 #include "alc/context.h"
26 #include "core/async_event.h"
27 #include "core/context.h"
28 #include "core/effects/base.h"
29 #include "core/logging.h"
31 #include "direct_defs.h"
33 #include "intrusive_ptr.h"
34 #include "opthelpers.h"
35 #include "ringbuffer.h"
40 template<typename
... Ts
>
41 struct overloaded
: Ts
... { using Ts::operator()...; };
43 template<typename
... Ts
>
44 overloaded(Ts
...) -> overloaded
<Ts
...>;
46 int EventThread(ALCcontext
*context
)
48 RingBuffer
*ring
{context
->mAsyncEvents
.get()};
52 auto evt_data
= ring
->getReadVector()[0];
55 context
->mEventSem
.wait();
59 auto eventlock
= std::lock_guard
{context
->mEventCbLock
};
60 const auto enabledevts
= context
->mEnabledEvts
.load(std::memory_order_acquire
);
61 auto evt_span
= al::span
{std::launder(reinterpret_cast<AsyncEvent
*>(evt_data
.buf
)),
63 for(auto &event
: evt_span
)
65 quitnow
= std::holds_alternative
<AsyncKillThread
>(event
);
66 if(quitnow
) UNLIKELY
break;
68 auto proc_killthread
= [](AsyncKillThread
&) { };
69 auto proc_release
= [](AsyncEffectReleaseEvent
&evt
)
71 al::intrusive_ptr
<EffectState
>{evt
.mEffectState
};
73 auto proc_srcstate
= [context
,enabledevts
](AsyncSourceStateEvent
&evt
)
76 || !enabledevts
.test(al::to_underlying(AsyncEnableBits::SourceState
)))
80 std::string msg
{"Source ID " + std::to_string(evt
.mId
)};
81 msg
+= " state has changed to ";
84 case AsyncSrcState::Reset
:
88 case AsyncSrcState::Stop
:
92 case AsyncSrcState::Play
:
96 case AsyncSrcState::Pause
:
101 context
->mEventCb(AL_EVENT_TYPE_SOURCE_STATE_CHANGED_SOFT
, evt
.mId
, state
,
102 al::sizei(msg
), msg
.c_str(), context
->mEventParam
);
104 auto proc_buffercomp
= [context
,enabledevts
](AsyncBufferCompleteEvent
&evt
)
106 if(!context
->mEventCb
107 || !enabledevts
.test(al::to_underlying(AsyncEnableBits::BufferCompleted
)))
110 std::string msg
{std::to_string(evt
.mCount
)};
111 if(evt
.mCount
== 1) msg
+= " buffer completed";
112 else msg
+= " buffers completed";
113 context
->mEventCb(AL_EVENT_TYPE_BUFFER_COMPLETED_SOFT
, evt
.mId
, evt
.mCount
,
114 al::sizei(msg
), msg
.c_str(), context
->mEventParam
);
116 auto proc_disconnect
= [context
,enabledevts
](AsyncDisconnectEvent
&evt
)
118 if(!context
->mEventCb
119 || !enabledevts
.test(al::to_underlying(AsyncEnableBits::Disconnected
)))
122 context
->mEventCb(AL_EVENT_TYPE_DISCONNECTED_SOFT
, 0, 0, al::sizei(evt
.msg
),
123 evt
.msg
.c_str(), context
->mEventParam
);
126 std::visit(overloaded
{proc_srcstate
, proc_buffercomp
, proc_release
, proc_disconnect
,
127 proc_killthread
}, event
);
129 std::destroy(evt_span
.begin(), evt_span
.end());
130 ring
->readAdvance(evt_span
.size());
135 constexpr std::optional
<AsyncEnableBits
> GetEventType(ALenum etype
) noexcept
139 case AL_EVENT_TYPE_BUFFER_COMPLETED_SOFT
: return AsyncEnableBits::BufferCompleted
;
140 case AL_EVENT_TYPE_DISCONNECTED_SOFT
: return AsyncEnableBits::Disconnected
;
141 case AL_EVENT_TYPE_SOURCE_STATE_CHANGED_SOFT
: return AsyncEnableBits::SourceState
;
149 void StartEventThrd(ALCcontext
*ctx
)
152 ctx
->mEventThread
= std::thread
{EventThread
, ctx
};
154 catch(std::exception
& e
) {
155 ERR("Failed to start event thread: %s\n", e
.what());
158 ERR("Failed to start event thread! Expect problems.\n");
162 void StopEventThrd(ALCcontext
*ctx
)
164 RingBuffer
*ring
{ctx
->mAsyncEvents
.get()};
165 auto evt_data
= ring
->getWriteVector()[0];
166 if(evt_data
.len
== 0)
169 std::this_thread::yield();
170 evt_data
= ring
->getWriteVector()[0];
171 } while(evt_data
.len
== 0);
173 std::ignore
= InitAsyncEvent
<AsyncKillThread
>(evt_data
.buf
);
174 ring
->writeAdvance(1);
176 ctx
->mEventSem
.post();
177 if(ctx
->mEventThread
.joinable())
178 ctx
->mEventThread
.join();
181 AL_API
DECL_FUNCEXT3(void, alEventControl
,SOFT
, ALsizei
,count
, const ALenum
*,types
, ALboolean
,enable
)
182 FORCE_ALIGN
void AL_APIENTRY
alEventControlDirectSOFT(ALCcontext
*context
, ALsizei count
,
183 const ALenum
*types
, ALboolean enable
) noexcept
186 throw al::context_error
{AL_INVALID_VALUE
, "Controlling %d events", count
};
187 if(count
<= 0) UNLIKELY
return;
190 throw al::context_error
{AL_INVALID_VALUE
, "NULL pointer"};
192 ContextBase::AsyncEventBitset flags
{};
193 for(ALenum evttype
: al::span
{types
, static_cast<uint
>(count
)})
195 auto etype
= GetEventType(evttype
);
197 throw al::context_error
{AL_INVALID_ENUM
, "Invalid event type 0x%04x", evttype
};
198 flags
.set(al::to_underlying(*etype
));
203 auto enabledevts
= context
->mEnabledEvts
.load(std::memory_order_relaxed
);
204 while(context
->mEnabledEvts
.compare_exchange_weak(enabledevts
, enabledevts
|flags
,
205 std::memory_order_acq_rel
, std::memory_order_acquire
) == 0)
207 /* enabledevts is (re-)filled with the current value on failure, so
214 auto enabledevts
= context
->mEnabledEvts
.load(std::memory_order_relaxed
);
215 while(context
->mEnabledEvts
.compare_exchange_weak(enabledevts
, enabledevts
&~flags
,
216 std::memory_order_acq_rel
, std::memory_order_acquire
) == 0)
219 /* Wait to ensure the event handler sees the changed flags before
222 std::lock_guard
<std::mutex
> eventlock
{context
->mEventCbLock
};
225 catch(al::context_error
& e
) {
226 context
->setError(e
.errorCode(), "%s", e
.what());
229 AL_API
DECL_FUNCEXT2(void, alEventCallback
,SOFT
, ALEVENTPROCSOFT
,callback
, void*,userParam
)
230 FORCE_ALIGN
void AL_APIENTRY
alEventCallbackDirectSOFT(ALCcontext
*context
,
231 ALEVENTPROCSOFT callback
, void *userParam
) noexcept
233 std::lock_guard
<std::mutex
> eventlock
{context
->mEventCbLock
};
234 context
->mEventCb
= callback
;
235 context
->mEventParam
= userParam
;