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"
35 #include "intrusive_ptr.h"
36 #include "opthelpers.h"
37 #include "ringbuffer.h"
42 template<typename
... Ts
>
43 struct overloaded
: Ts
... { using Ts::operator()...; };
45 template<typename
... Ts
>
46 overloaded(Ts
...) -> overloaded
<Ts
...>;
48 int EventThread(ALCcontext
*context
)
50 RingBuffer
*ring
{context
->mAsyncEvents
.get()};
54 auto evt_data
= ring
->getReadVector().first
;
57 context
->mEventSem
.wait();
61 std::lock_guard
<std::mutex
> eventlock
{context
->mEventCbLock
};
62 auto evt_span
= al::span
{std::launder(reinterpret_cast<AsyncEvent
*>(evt_data
.buf
)),
64 for(auto &event
: evt_span
)
66 quitnow
= std::holds_alternative
<AsyncKillThread
>(event
);
67 if(quitnow
) UNLIKELY
break;
69 auto enabledevts
= context
->mEnabledEvts
.load(std::memory_order_acquire
);
70 auto proc_killthread
= [](AsyncKillThread
&) { };
71 auto proc_release
= [](AsyncEffectReleaseEvent
&evt
)
73 al::intrusive_ptr
<EffectState
>{evt
.mEffectState
};
75 auto proc_srcstate
= [context
,enabledevts
](AsyncSourceStateEvent
&evt
)
78 || !enabledevts
.test(al::to_underlying(AsyncEnableBits::SourceState
)))
82 std::string msg
{"Source ID " + std::to_string(evt
.mId
)};
83 msg
+= " state has changed to ";
86 case AsyncSrcState::Reset
:
90 case AsyncSrcState::Stop
:
94 case AsyncSrcState::Play
:
98 case AsyncSrcState::Pause
:
103 context
->mEventCb(AL_EVENT_TYPE_SOURCE_STATE_CHANGED_SOFT
, evt
.mId
, state
,
104 static_cast<ALsizei
>(msg
.length()), msg
.c_str(), context
->mEventParam
);
106 auto proc_buffercomp
= [context
,enabledevts
](AsyncBufferCompleteEvent
&evt
)
108 if(!context
->mEventCb
109 || !enabledevts
.test(al::to_underlying(AsyncEnableBits::BufferCompleted
)))
112 std::string msg
{std::to_string(evt
.mCount
)};
113 if(evt
.mCount
== 1) msg
+= " buffer completed";
114 else msg
+= " buffers completed";
115 context
->mEventCb(AL_EVENT_TYPE_BUFFER_COMPLETED_SOFT
, evt
.mId
, evt
.mCount
,
116 static_cast<ALsizei
>(msg
.length()), msg
.c_str(), context
->mEventParam
);
118 auto proc_disconnect
= [context
,enabledevts
](AsyncDisconnectEvent
&evt
)
120 const std::string_view message
{evt
.msg
.data()};
122 context
->debugMessage(DebugSource::System
, DebugType::Error
, 0,
123 DebugSeverity::High
, message
);
126 && enabledevts
.test(al::to_underlying(AsyncEnableBits::Disconnected
)))
127 context
->mEventCb(AL_EVENT_TYPE_DISCONNECTED_SOFT
, 0, 0,
128 static_cast<ALsizei
>(message
.length()), message
.data(),
129 context
->mEventParam
);
132 std::visit(overloaded
{proc_srcstate
, proc_buffercomp
, proc_release
, proc_disconnect
,
133 proc_killthread
}, event
);
135 std::destroy(evt_span
.begin(), evt_span
.end());
136 ring
->readAdvance(evt_span
.size());
141 constexpr std::optional
<AsyncEnableBits
> GetEventType(ALenum etype
) noexcept
145 case AL_EVENT_TYPE_BUFFER_COMPLETED_SOFT
: return AsyncEnableBits::BufferCompleted
;
146 case AL_EVENT_TYPE_DISCONNECTED_SOFT
: return AsyncEnableBits::Disconnected
;
147 case AL_EVENT_TYPE_SOURCE_STATE_CHANGED_SOFT
: return AsyncEnableBits::SourceState
;
155 void StartEventThrd(ALCcontext
*ctx
)
158 ctx
->mEventThread
= std::thread
{EventThread
, ctx
};
160 catch(std::exception
& e
) {
161 ERR("Failed to start event thread: %s\n", e
.what());
164 ERR("Failed to start event thread! Expect problems.\n");
168 void StopEventThrd(ALCcontext
*ctx
)
170 RingBuffer
*ring
{ctx
->mAsyncEvents
.get()};
171 auto evt_data
= ring
->getWriteVector().first
;
172 if(evt_data
.len
== 0)
175 std::this_thread::yield();
176 evt_data
= ring
->getWriteVector().first
;
177 } while(evt_data
.len
== 0);
179 std::ignore
= InitAsyncEvent
<AsyncKillThread
>(evt_data
.buf
);
180 ring
->writeAdvance(1);
182 ctx
->mEventSem
.post();
183 if(ctx
->mEventThread
.joinable())
184 ctx
->mEventThread
.join();
187 AL_API
DECL_FUNCEXT3(void, alEventControl
,SOFT
, ALsizei
,count
, const ALenum
*,types
, ALboolean
,enable
)
188 FORCE_ALIGN
void AL_APIENTRY
alEventControlDirectSOFT(ALCcontext
*context
, ALsizei count
,
189 const ALenum
*types
, ALboolean enable
) noexcept
191 if(count
< 0) context
->setError(AL_INVALID_VALUE
, "Controlling %d events", count
);
192 if(count
<= 0) return;
193 if(!types
) return context
->setError(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 return context
->setError(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
};
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
;