I'm writing a native WebRTC application that forwards pre-encoded frames for a client right now. That part is all fine, but I'm having segfault issues every time I attempt to exit my application, specifically with regards to how I'm destroying my WebRtcPeerConnectionFactory.
I'm instantiating this object by first launching separate threads for networking, signaling, and working respectively, and in my destructor kill these threads by calling thread->Quit() before setting my webRtcPeerConnectionFactory to a nullptr (as I've seen in examples in the source code do in their conductor.cc files), but I either segfault or hang indefinitely depending on the order with which the prior two actions are taken.
On a high level is there a correct way to gracefully destroy the factory object or is there some cleanup function I'm not calling? I can't find any other examples online that take advantage of the WebRTC threading model so I'm not sure where to move on from here. Thanks!
My instantiation of the object is performed like so:
rtc_network_thread_ = rtc::Thread::CreateWithSocketServer();
rtc_worker_thread_ = rtc::Thread::Create();
rtc_signaling_thread_ = rtc::Thread::Create();
if (!rtc_network_thread_->Start() || !rtc_worker_thread_->Start() || !rtc_signaling_thread->Start()) {
// error handling
}
peer_connection_factory_ = webrtc::CreatePeerConnectionFactory(
rtc_network_thread_.get(), rtc_worker_thread_.get(), rtc_signaling_thread_.get(),
nullptr, webrtc::CreateBuiltInAudioEncoderFactory(), webrtc::CreateBuiltInAudioDecoderFactory(),
dummy_encoder_factory_.get(), nullptr)
And my subsequent cleanup looks like this:
rtc_worker_thread_->Quit();
rtc_network_thread_->Quit();
rtc_signaling_thread_->Quit();
if (peer_connection_factory_) {
// errors occur here, either with setting to nullptr or with threads
// quitting if I quit the threads after setting my factory to a nullptr
peer_connection_factory_ = nullptr;
}
Related
I have the following code snippet, that downloads an about 100mb big zip file that then will be deserialzed into a Foo for further processing. This code is execcuted once per day.
Stream stream = await downloader.DownloadStream().ConfigureAwait(false);
if (stream.CanSeek && stream.Length == 0)
{
throw new IOException("Error! Received stream is empty.");
}
ZipArchive archive;
try
{
archive = new ZipArchive(stream);
}
catch (InvalidDataException)
{
throw new IOException("Error! Received stream is no zip file.");
}
using (var reader = new StreamReader(archive.Entries[0].Open()))
{
Foo foo = JsonConvert.DeserializeObject<Foo>(reader.ReadToEnd());
[...]
}
archive.Dispose();
stream.Dispose();
Now, when that code kicks in, the memory usage jumps as high as about 2500mb. So far so good, and I would say, pretty common.
The (maybe?) uncommon thing is, that the memory sometimes takes days to be released. Now, when running that code on multiple instances, let's say inside k8s, you would see the following pattern of memory usage.
So you see, the memory is eventually released, so I doubt I have some error inside the code regarding a possible leak. The question is if this is expected behavior of the clr or if I maybe can enhance this behavior. What I already read is that this would definitely involve the large object heap. But it's hard do find internals how that heap behaves natively under linux.
So for hosting this in k8s, this means that I need to plan the resources for the worst case scenario, that all hosted instances will have about 2500mb allocated.
I'd like to organize a thread barrier: given a single lock object, any thread can obtain it and continue thread's chain further, but any other thread will stay dormant on the same lock object until the first thread finishes and releases the lock.
Let's express my intention in code (log() simply prints string in a log):
val mutex = Semaphore(1) // number of permits is 1
source
.subscribeOn(Schedulers.newThread()) // any unbound scheduler (io, newThread)
.flatMap {
log("#1")
mutex.acquireUninterruptibly()
log("#2")
innerSource
.doOnSubscribe(log("#3"))
.doFinally {
mutex.release()
log("#4")
}
}
.subscribe()
It actually works well, i can see how multiple threads show log "#1" and only one of them propagates further, obtaining lock object mutex, then it releases it and i can see other logs, and next threads comes into play. OK
But sometimes, when pressure is quite high and number of threads is greater, say 4-5, i experience DEADLOCK:
Actually, the thread that has acquired the lock, prints "#1" and "#2" but it then never print "#3" (so doOnSubscribe() not called), so it actually stops and does nothing, not subscribing to innerSource in flatMap. So all threads are blocked and app is not responsive at all.
My question - is it safe to have blocking operation inside flatMap? I dig into flatMap source code and i see the place where it internally subscribes:
if (!isDisposed()) {
o.subscribe(new FlatMapSingleObserver<R>(this, downstream));
}
Is it possible that thread's subscription, that has acquired lock, was disposed somehow?
You can use flatMap second parameter maxConcurrency and set it to 1, so it does what you want without manually locking
I am using VisualGFX to develop a user interface for my STM32F429i-Discovery board. VisualGFX has built in freeRTOS usage with a GUITask being created to handle the GUI operations. I am then trying to create a new task called ControllerTask with the xTaskCreate API function from freeRTOS, same function as is used to create the GUITask.
However, as soon as I create this second Task the GUITask displays some weird values on my GUI and there is no functionality.
int main(void){
...
xTaskCreate(GUITask, (TASKCREATE_NAME_TYPE)"GUITask",
configGUI_TASK_STK_SIZE,
NULL,
configGUI_TASK_PRIORITY,
NULL);
xTaskCreate (ControllerTask, (TASKCREATE_NAME_TYPE)"ControllerTask",
configController_TASK_STK_SIZE,
NULL,
configController_TASK_PRIORITY,
NULL);
vTaskStartScheduler();
for (;;);
The above code shows the creation of the two tasks. The priority of the GUITask is higher as that of the ControllerTask. The next piece of code shows the Task implementation.
static void GUITask(void* params)
{
touchgfx::HAL::getInstance()->taskEntry();
}
static void ControllerTask(void* params)
{
while(1)
{
vTaskDelay(3000);
}
}
As can be seen the implementation of the ControllerTask at this moment in time is only Delaying the Task for roughly 3 seconds each time it is switched to.
However, the GUITask gets stuck and no GUI updates or interaction is possible.
Basically what is likely to happen is that your taskEntry() function never returns, and thus never gives back processing time to the kernel by using vTaskDelay or other Queue calls.
void taskEntry ( ) virtual Main event loop. Will wait for VSYNC
signal, and then process next frame. Call this function from your GUI
task.
Note This function never returns! Reimplemented in HALSDL2, and
HALSDL.
If both task has same priority, or if your touchgfx has higher priority, you will get stuck.
To solve that issue, there are two ways.
Is to implement the task with priorities, and set that task with the lowest priority of all.
Enable the Idle task and implement the call on that method.
Following the guidelines here I'm able to set the "consumer_cancel_notify" property for my client connection, but when the Queue is deleted the client still isn't noticing. I'm guessing that I probably have to override some method or set a callback somewhere, but after digging through the source code I'm lost as to where I'd do this. Does anybody offhand know where I'd listen for this notification?
Ok here's how I got it to work:
When creating the Queue (i.e. "declaring" the Queue), add a callback for the "AMQP_CANCEL" messages.
Inside AMQPQueue::sendConsumeCommand(), inside the while(1) loop where the code checks for the different *frame.payload.method.id*s, add a check for the AMQP_BASIC_CANCEL_METHOD, e.g.
if (frame.payload.method.id == AMQP_BASIC_CANCEL_METHOD){
cout << "AMQP_BASIC_CANCEL_METHOD received" << endl;
if ( events.find(AMQP_CANCEL) != events.end() ) {
(*events[AMQP_CANCEL])(pmessage);
}
continue;
}
That's it.
For my purposes I wanted to redeclare the Queue if it got deleted so that I could keep consuming messages, so inside my callback I just redeclared the queue, set up the bindings, added events, set the consumer tag, and consumed.
I am using lua coroutines (lua 5.1) to create a plugin system for an application. I was hoping to use coroutines so that the plugin could operate as if it were a separate application program which yields once per processing frame. The plugin programs generally follow a formula something like:
function Program(P)
-- setup --
NewDrawer(function()
-- this gets rendered in a window for this plugin program --
drawstuff(howeveryouwant)
end)
-- loop --
local continue = true
while continue do
-- frame by frame stuff excluding rendering (handled by NewDrawer) --
P = coroutine.yield()
end
end
Each plugin is resumed in the main loop of the application once per frame. Then when drawing begins each plugin has an individual window it draws in which is when the function passed to NewDrawer is executed.
Something like this:
while MainContinue do
-- other stuff left out --
ExecutePluginFrames() -- all plugin coroutines resumed once
BeginRendering()
-- other stuff left out --
RenderPluginWindows() -- functions passed to NewDrawer called.
EndRendering()
end
However I found that this suddenly began acting strangely and messing up my otherwise robust error handling system whenever an error occurred in the rendering. It took me a little while to wrap my head around what was happening but it seems that the call to WIN:Draw() which I expected to be in the main thread's call stack (because it is handled by the main application) was actually causing an implicit jump into the coroutine's call stack.
At first the issue was that the program was closing suddenly with no useful error output. Then after looking at a stack traceback of the rendering function defined in the plugin program I saw that everything leading up to the window's Draw from the main thread was not there and that yield was in the call stack.
It seems that because the window was created in the thread and the drawing function, that they are being handled by that thread's call stack, which is a problem because it means they are outside of the pcall set up in the main thread.
Is this suppose to happen? is it the result of a bug/shortcut in the C source? am I doing something wrong or at least not correctly enough? is there a way to handle this cleanly?
I can't reproduce the effect you are describing. This is the code I'm running:
local drawer = {}
function NewDrawer(func)
table.insert(drawer, func)
end
function Program(P)
NewDrawer(function()
print("inside program", P)
end)
-- loop --
local continue = true
while continue do
-- frame by frame stuff excluding rendering (handled by NewDrawer) --
P = coroutine.yield()
end
end
local coro = coroutine.create(Program)
local MainContinue = true
while MainContinue do
-- other stuff left out --
-- ExecutePluginFrames() -- all plugin coroutines resumed once
coroutine.resume(coro, math.random(10))
-- RenderPluginWindows() -- functions passed to NewDrawer called.
for _, plugin in ipairs(drawer) do
plugin()
end
MainContinue = false
end
When I step through the code and look at the stack, the callback that is set in NewDrawer is called in the "main" thread as it should. You can see it yourself if you call coroutine.running() which returns the current thread or nil if you are inside the main thread.
I have discovered why this was happening in my case. The render objects which called the function passed to NewDrawer are initialized on creation (by the c code) with a pointer to the lua state that created them and this is used for accessing their associated lua data and for calling the draw function. I had not seen the connection between lua_State and coroutines. So as it turns out it is possible for functions to be called in the stack after yield if C code is causing them.
As far as a solution goes I've decided to break the program into two coroutines, one for rendering and one for processing. This fixes the problem by allowing the creating thread of the render objects to also be the calling thread, and keeps the neat advantages of the independence of the rendering loop and processing loop.