15 #include <string_view>
25 #include "alc/context.h"
26 #include "alc/inprogext.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 std::lock_guard
<std::mutex
> eventlock
{context
->mEventCbLock
};
63 auto evt_span
= al::span
{std::launder(reinterpret_cast<AsyncEvent
*>(evt_data
.buf
)),
65 for(auto &event
: evt_span
)
67 quitnow
= std::holds_alternative
<AsyncKillThread
>(event
);
68 if(quitnow
) UNLIKELY
break;
70 auto enabledevts
= context
->mEnabledEvts
.load(std::memory_order_acquire
);
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 static_cast<ALsizei
>(msg
.length()), 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 static_cast<ALsizei
>(msg
.length()), msg
.c_str(), context
->mEventParam
);
119 auto proc_disconnect
= [context
,enabledevts
](AsyncDisconnectEvent
&evt
)
121 const std::string_view message
{evt
.msg
.data()};
123 context
->debugMessage(DebugSource::System
, DebugType::Error
, 0,
124 DebugSeverity::High
, message
);
127 && enabledevts
.test(al::to_underlying(AsyncEnableBits::Disconnected
)))
128 context
->mEventCb(AL_EVENT_TYPE_DISCONNECTED_SOFT
, 0, 0,
129 static_cast<ALsizei
>(message
.length()), message
.data(),
130 context
->mEventParam
);
133 std::visit(overloaded
{proc_srcstate
, proc_buffercomp
, proc_release
, proc_disconnect
,
134 proc_killthread
}, event
);
136 std::destroy(evt_span
.begin(), evt_span
.end());
137 ring
->readAdvance(evt_span
.size());
142 constexpr std::optional
<AsyncEnableBits
> GetEventType(ALenum etype
) noexcept
146 case AL_EVENT_TYPE_BUFFER_COMPLETED_SOFT
: return AsyncEnableBits::BufferCompleted
;
147 case AL_EVENT_TYPE_DISCONNECTED_SOFT
: return AsyncEnableBits::Disconnected
;
148 case AL_EVENT_TYPE_SOURCE_STATE_CHANGED_SOFT
: return AsyncEnableBits::SourceState
;
156 void StartEventThrd(ALCcontext
*ctx
)
159 ctx
->mEventThread
= std::thread
{EventThread
, ctx
};
161 catch(std::exception
& e
) {
162 ERR("Failed to start event thread: %s\n", e
.what());
165 ERR("Failed to start event thread! Expect problems.\n");
169 void StopEventThrd(ALCcontext
*ctx
)
171 RingBuffer
*ring
{ctx
->mAsyncEvents
.get()};
172 auto evt_data
= ring
->getWriteVector().first
;
173 if(evt_data
.len
== 0)
176 std::this_thread::yield();
177 evt_data
= ring
->getWriteVector().first
;
178 } while(evt_data
.len
== 0);
180 std::ignore
= InitAsyncEvent
<AsyncKillThread
>(evt_data
.buf
);
181 ring
->writeAdvance(1);
183 ctx
->mEventSem
.post();
184 if(ctx
->mEventThread
.joinable())
185 ctx
->mEventThread
.join();
188 AL_API
DECL_FUNCEXT3(void, alEventControl
,SOFT
, ALsizei
,count
, const ALenum
*,types
, ALboolean
,enable
)
189 FORCE_ALIGN
void AL_APIENTRY
alEventControlDirectSOFT(ALCcontext
*context
, ALsizei count
,
190 const ALenum
*types
, ALboolean enable
) noexcept
193 throw al::context_error
{AL_INVALID_VALUE
, "Controlling %d events", count
};
194 if(count
<= 0) UNLIKELY
return;
197 throw al::context_error
{AL_INVALID_VALUE
, "NULL pointer"};
199 ContextBase::AsyncEventBitset flags
{};
200 for(ALenum evttype
: al::span
{types
, static_cast<uint
>(count
)})
202 auto etype
= GetEventType(evttype
);
204 throw al::context_error
{AL_INVALID_ENUM
, "Invalid event type 0x%04x", evttype
};
205 flags
.set(al::to_underlying(*etype
));
210 auto enabledevts
= context
->mEnabledEvts
.load(std::memory_order_relaxed
);
211 while(context
->mEnabledEvts
.compare_exchange_weak(enabledevts
, enabledevts
|flags
,
212 std::memory_order_acq_rel
, std::memory_order_acquire
) == 0)
214 /* enabledevts is (re-)filled with the current value on failure, so
221 auto enabledevts
= context
->mEnabledEvts
.load(std::memory_order_relaxed
);
222 while(context
->mEnabledEvts
.compare_exchange_weak(enabledevts
, enabledevts
&~flags
,
223 std::memory_order_acq_rel
, std::memory_order_acquire
) == 0)
226 /* Wait to ensure the event handler sees the changed flags before
229 std::lock_guard
<std::mutex
> eventlock
{context
->mEventCbLock
};
232 catch(al::context_error
& e
) {
233 context
->setError(e
.errorCode(), "%s", e
.what());
236 AL_API
DECL_FUNCEXT2(void, alEventCallback
,SOFT
, ALEVENTPROCSOFT
,callback
, void*,userParam
)
237 FORCE_ALIGN
void AL_APIENTRY
alEventCallbackDirectSOFT(ALCcontext
*context
,
238 ALEVENTPROCSOFT callback
, void *userParam
) noexcept
240 std::lock_guard
<std::mutex
> eventlock
{context
->mEventCbLock
};
241 context
->mEventCb
= callback
;
242 context
->mEventParam
= userParam
;