15 #include <string_view>
25 #include "alc/context.h"
29 #include "core/async_event.h"
30 #include "core/context.h"
31 #include "core/effects/base.h"
32 #include "core/logging.h"
34 #include "direct_defs.h"
36 #include "intrusive_ptr.h"
37 #include "opthelpers.h"
38 #include "ringbuffer.h"
43 template<typename
... Ts
>
44 struct overloaded
: Ts
... { using Ts::operator()...; };
46 template<typename
... Ts
>
47 overloaded(Ts
...) -> overloaded
<Ts
...>;
49 int EventThread(ALCcontext
*context
)
51 RingBuffer
*ring
{context
->mAsyncEvents
.get()};
55 auto evt_data
= ring
->getReadVector().first
;
58 context
->mEventSem
.wait();
62 auto eventlock
= std::lock_guard
{context
->mEventCbLock
};
63 const auto enabledevts
= context
->mEnabledEvts
.load(std::memory_order_acquire
);
64 auto evt_span
= al::span
{std::launder(reinterpret_cast<AsyncEvent
*>(evt_data
.buf
)),
66 for(auto &event
: evt_span
)
68 quitnow
= std::holds_alternative
<AsyncKillThread
>(event
);
69 if(quitnow
) UNLIKELY
break;
71 auto proc_killthread
= [](AsyncKillThread
&) { };
72 auto proc_release
= [](AsyncEffectReleaseEvent
&evt
)
74 al::intrusive_ptr
<EffectState
>{evt
.mEffectState
};
76 auto proc_srcstate
= [context
,enabledevts
](AsyncSourceStateEvent
&evt
)
79 || !enabledevts
.test(al::to_underlying(AsyncEnableBits::SourceState
)))
83 std::string msg
{"Source ID " + std::to_string(evt
.mId
)};
84 msg
+= " state has changed to ";
87 case AsyncSrcState::Reset
:
91 case AsyncSrcState::Stop
:
95 case AsyncSrcState::Play
:
99 case AsyncSrcState::Pause
:
104 context
->mEventCb(AL_EVENT_TYPE_SOURCE_STATE_CHANGED_SOFT
, evt
.mId
, state
,
105 al::sizei(msg
), msg
.c_str(), context
->mEventParam
);
107 auto proc_buffercomp
= [context
,enabledevts
](AsyncBufferCompleteEvent
&evt
)
109 if(!context
->mEventCb
110 || !enabledevts
.test(al::to_underlying(AsyncEnableBits::BufferCompleted
)))
113 std::string msg
{std::to_string(evt
.mCount
)};
114 if(evt
.mCount
== 1) msg
+= " buffer completed";
115 else msg
+= " buffers completed";
116 context
->mEventCb(AL_EVENT_TYPE_BUFFER_COMPLETED_SOFT
, evt
.mId
, evt
.mCount
,
117 al::sizei(msg
), msg
.c_str(), context
->mEventParam
);
119 auto proc_disconnect
= [context
,enabledevts
](AsyncDisconnectEvent
&evt
)
121 if(!context
->mEventCb
122 || !enabledevts
.test(al::to_underlying(AsyncEnableBits::Disconnected
)))
125 context
->mEventCb(AL_EVENT_TYPE_DISCONNECTED_SOFT
, 0, 0, al::sizei(evt
.msg
),
126 evt
.msg
.c_str(), context
->mEventParam
);
129 std::visit(overloaded
{proc_srcstate
, proc_buffercomp
, proc_release
, proc_disconnect
,
130 proc_killthread
}, event
);
132 std::destroy(evt_span
.begin(), evt_span
.end());
133 ring
->readAdvance(evt_span
.size());
138 constexpr std::optional
<AsyncEnableBits
> GetEventType(ALenum etype
) noexcept
142 case AL_EVENT_TYPE_BUFFER_COMPLETED_SOFT
: return AsyncEnableBits::BufferCompleted
;
143 case AL_EVENT_TYPE_DISCONNECTED_SOFT
: return AsyncEnableBits::Disconnected
;
144 case AL_EVENT_TYPE_SOURCE_STATE_CHANGED_SOFT
: return AsyncEnableBits::SourceState
;
152 void StartEventThrd(ALCcontext
*ctx
)
155 ctx
->mEventThread
= std::thread
{EventThread
, ctx
};
157 catch(std::exception
& e
) {
158 ERR("Failed to start event thread: %s\n", e
.what());
161 ERR("Failed to start event thread! Expect problems.\n");
165 void StopEventThrd(ALCcontext
*ctx
)
167 RingBuffer
*ring
{ctx
->mAsyncEvents
.get()};
168 auto evt_data
= ring
->getWriteVector().first
;
169 if(evt_data
.len
== 0)
172 std::this_thread::yield();
173 evt_data
= ring
->getWriteVector().first
;
174 } while(evt_data
.len
== 0);
176 std::ignore
= InitAsyncEvent
<AsyncKillThread
>(evt_data
.buf
);
177 ring
->writeAdvance(1);
179 ctx
->mEventSem
.post();
180 if(ctx
->mEventThread
.joinable())
181 ctx
->mEventThread
.join();
184 AL_API
DECL_FUNCEXT3(void, alEventControl
,SOFT
, ALsizei
,count
, const ALenum
*,types
, ALboolean
,enable
)
185 FORCE_ALIGN
void AL_APIENTRY
alEventControlDirectSOFT(ALCcontext
*context
, ALsizei count
,
186 const ALenum
*types
, ALboolean enable
) noexcept
189 throw al::context_error
{AL_INVALID_VALUE
, "Controlling %d events", count
};
190 if(count
<= 0) UNLIKELY
return;
193 throw al::context_error
{AL_INVALID_VALUE
, "NULL pointer"};
195 ContextBase::AsyncEventBitset flags
{};
196 for(ALenum evttype
: al::span
{types
, static_cast<uint
>(count
)})
198 auto etype
= GetEventType(evttype
);
200 throw al::context_error
{AL_INVALID_ENUM
, "Invalid event type 0x%04x", evttype
};
201 flags
.set(al::to_underlying(*etype
));
206 auto enabledevts
= context
->mEnabledEvts
.load(std::memory_order_relaxed
);
207 while(context
->mEnabledEvts
.compare_exchange_weak(enabledevts
, enabledevts
|flags
,
208 std::memory_order_acq_rel
, std::memory_order_acquire
) == 0)
210 /* enabledevts is (re-)filled with the current value on failure, so
217 auto enabledevts
= context
->mEnabledEvts
.load(std::memory_order_relaxed
);
218 while(context
->mEnabledEvts
.compare_exchange_weak(enabledevts
, enabledevts
&~flags
,
219 std::memory_order_acq_rel
, std::memory_order_acquire
) == 0)
222 /* Wait to ensure the event handler sees the changed flags before
225 std::lock_guard
<std::mutex
> eventlock
{context
->mEventCbLock
};
228 catch(al::context_error
& e
) {
229 context
->setError(e
.errorCode(), "%s", e
.what());
232 AL_API
DECL_FUNCEXT2(void, alEventCallback
,SOFT
, ALEVENTPROCSOFT
,callback
, void*,userParam
)
233 FORCE_ALIGN
void AL_APIENTRY
alEventCallbackDirectSOFT(ALCcontext
*context
,
234 ALEVENTPROCSOFT callback
, void *userParam
) noexcept
236 std::lock_guard
<std::mutex
> eventlock
{context
->mEventCbLock
};
237 context
->mEventCb
= callback
;
238 context
->mEventParam
= userParam
;