Aborting worker object in QThread - qt5

I know that there are many posts on this topic and i've read them and thought i also understood them. But I am still having a problem with aborting a QThread or rather a worker object in a QThread.
I have an GUI application and a library. The GUI can request the library to exec and abort worker objects (is connected to WorkerHandler slots). The WorkerHandler can create several WorkerObjects that all inherit from a base class. I tried to reduce the code for this example, but it's still some kind of verbose.
Gui.h
class Gui : public QMainWindow
{
Q_OBJECT
public:
Gui(QWidget *parent = 0);
~Gui();
private:
Ui::GuiClass ui;
QThread *workerHandlerThread;
WorkerHandler *workerHandler;
void connectActions();
signals:
void execWorker(WorkerParams _params);
void abortWorker(WorkerType type);
slots:
void buttonExecPressed();
void buttonAbortPressed();
}
Gui.cpp
void Gui::Gui()
{
ui.btnExecA->setProperty("type", QVariant::fromValue(WorkerType::A)); //WorkerType is just a enum, bin type to button
ui.btnExecB->setProperty("type", QVariant::fromValue(WorkerType::B));
ui.btnAbortA->setProperty("type", QVariant::fromValue(WorkerType::A));
ui.btnAbortB->setProperty("type", QVariant::fromValue(WorkerType::B));
connectActions();
workerHandlerThread = new QThread();
workerHandler = new WorkerHandler();
workerHandler->moveToThread(workerHandlerThread); // move worker execution to another thread
workerHandlerThread->start(); //start will call run and run will run the QEventLoop of QThread by calling exec
}
void Gui::~Gui()
{
workerHandlerThread->quit();
workerHandlerThread->wait();
delete workerHandlerThread;
delete workerHandler;
}
void Gui::connectActions()
{
connect(ui.btnExecA, &QPushButton::clicked, this, &Gui::buttonExecPressed);
connect(ui.btnExecB, &QPushButton::clicked, this, &Gui::buttonExecPressed);
connect(ui.btnAbortA, &QPushButton::clicked, this, &Gui::buttonAbortPressed);
connect(ui.btnAbortB, &QPushButton::clicked, this, &Gui::buttonAbortPressed);
connect(this, &Gui::execWorker, workerHandler, &WorkerHandler::execWorker);
connect(this, &Gui::abortWorker, workerHandler, &WorkerHandler::abortWorker);
}
void Gui::buttonExecPressed()
{
QPushButton* button = qobject_cast<QPushButton*>(sender());
if (button)
{
WorkerType type = button->property("type").value<WorkerType>(); //get worker type
WorkerParams params = WorkerParamsFactory::Get()->CreateParams(type); //WorkerParamsFactory cretes default parameters based on type
emit execWorker(params); //tell WorkerHandler to create a workerObject based on these parameters
}
}
void Gui::buttonAbortPressed()
{
QPushButton* button = qobject_cast<QPushButton*>(sender());
if (button)
{
WorkerType type = button->property("type").value<WorkerType>();
emit abortWorker(type); //tell WorkerHandler to abort a specific workerObject
}
}
WorkerHandler.h
class WorkerHandler : public QObject {
Q_OBJECT
public:
WorkerHandler(QObject * parent = Q_NULLPTR);
~WorkerHandler();
public slots:
void execWorker(WorkerParams _params);
void abortWorker(WorkerType type);
private:
QMap<WorkerType, WorkerObjectBase*> workerPool; //contains the workerobjects
};
WorkerHandler.cpp
void WorkerHandler::execWorker(WorkerParams _params)
{
QThread *thread = new QThread();
WorkerObjectBase *worker = WorkerObjectFactory::Get()->CreateWorker(_params); //Factory to create specific Worker Object based on given params
worker->moveToThread(thread);
connect(thread, &QThread::started, workerThread, &WorkerObjectBase::process);
connect(workerThread, &WorkerObjectBase::workerFinished, thread, &QThread::quit); //quit the QThread when worker is finished
connect(thread, &QThread::finished, thread, &QThread::deleteLater); //free resources when thread is finished
connect(thread, &QThread::finished, workerThread, &WorkerObjectBase::deleteLater); //free resources when thread is finished
workerPool.insert(_params.type, worker); //_params.type contains WorkerType
thread->start(); //will call run of qthread which will call exec
}
void WorkerHandler::abortWorker(WorkerType type)
{
WorkerObjectBase *worker = workerPool.value(type);
worker->requestAbort();
QThread *workerThread = worker->thread();
if (workerThread)
{
if (!workerThread->wait(10000)) //will always block the 10 seconds and terminate the thread. using just wait() will block forever
{
workerThread->terminate();
}
}
}
WorkerHandlerBase.h
class WorkerObjectBase : public QObject {
Q_OBJECT
public:
WorkerObjectBase(QObject * parent = Q_NULLPTR);
~WorkerObjectBase();
void requestAbort();
protected:
//some WorkerObject basic parameters
bool abortRequested();
public slots:
virtual void process();
signals:
void workerFinished();
private:
QMutex abortMutex;
bool abort = false;
};
WorkerHandlerBase.cpp
void WorkerObjectBase::requestAbort()
{
abortMutex.lock();
abort = true;
abortMutex.unlock();
}
bool WorkerObjectBase::abortRequested()
{
bool abortRequested;
abortMutex.lock();
abortRequested = abort;
abortMutex.unlock();
return abortRequested;
}
WorkerObjectA.h
class WorkerObjectA : public WorkerObjectBase {
Q_OBJECT
public:
WorkerObjectA(QObject * parent = Q_NULLPTR);
~WorkerObjectA();
protected:
//some WorkerObjectA parameters
public slots:
void process();
};
WorkerObjectA.cpp
void WorkerObjectA::process()
{
while(!abortRequested())
{
//do some stuff
}
emit workerFinished();
}
The problem is, when i use wait, it blocks the signal processing. workerFinished is not handled and QThread does not quit. But I still don't get why. When i create a new worker object, i move it to a different thread. When this thread is started, it runs its own QEventLoop as stated in QThread
5.5 Documentation:
void QThread::run()
The starting point for the thread. After calling start(), the newly
created thread calls this function. The default implementation simply
calls exec().
So even if my WorkerHandler thread is blocking because of calling wait, the QThread of the specific workerObject should still manage to get the workerFinished signal and call the quit slot. If i don't use wait at all, everything is fine. But when something unexpected happens in the worker object process method that keeps it from emitting workerFinished, i want to be able to kill the thread the hard way.
So, what am i doing wrong?

Related

Handling QEventloop and QThread

Can we create our own event loop inside QThread inherited class ? Is it a good approach as QThread itself have its own event loop. I am looing for a good approach to implement thread which exists life time of the application which should be also capable of emitting signals to another thread. I am little confused on the usage of QEventloop. This is my current implementation. Can someone suggest is this a good approach ?
class MyWorker: public QThread
{
public :
void run() {
while (notExit)
{
evLoop->processEvents(QEventLoop::AllEvents, 10);
}
}
void post(int eventMsg){
QMetaObject::invokeMethod(this, [this, eventMsg] {
onMessage(int eventMsg);
}, Qt::QueuedConnection);
}
void onMessage(int eventMsg)
{
if(eventMsg == 1) { evLoop->quit(); }
}
};

Lock-free thread safety in console apps

To ensure thread-safety, I'm trying to find a generic cross-platform approach to
execute all delegates asynchronously in the main thread or ...
execute delegete in a background thread and pass result to the main one
Considering that console apps do not have synchronization context, I create new context when app is loading and then use one of the following methods.
Set and restore custom SC as described in Await, SynchronizationContext, and Console Apps article by Stephen Toub
Marshall all delegates to main thread using context.Post call as described in the article ExecutionContext vs SynchronizationContext by Stephen Toub
Using background thread with producer-consumer collection as described in Basic synchronization by Joe Albahari
Question
Ideas #1 and #2 set context correctly only if it's done synchronously. If they're called from inside Parallel.For(0, 100) then synchronization context starts using all threads available in a thread pool. Idea #3 always performs tasks within dedicated thread as expected, unfortunately, not in the main thread. Combining idea #3 with IOCompletionPortTaskScheduler, I can achieve asynchrony and single-threading, unfortunately, this approach will work only in Windows. Is there a way to combine these solutions to achieve requirements at the top of the post, including cross-platform.
Scheduler
public class SomeScheduler
{
public Task<T> RunInTheMainThread<T>(Func<T> action, SynchronizationContext sc)
{
var res = new TaskCompletionSource<T>();
SynchronizationContext.SetSynchronizationContext(sc); // Idea #1
sc.Post(o => res.SetResult(action()), null); // Idea #2
ThreadPool.QueueUserWorkItem(state => res.SetResult(action())); // Idea #3
return res.Task;
}
}
Main
var scheduler = new SomeScheduler();
var sc = SynchronizationContext.Current ?? new SynchronizationContext();
new Thread(async () =>
{
var res = await scheduler.ExecuteAsync(() => 5, sc);
});
You can use the lock/Monitor.Pulse/Monitor.Wait and a Queue
I know the title says lock-free. But my guess is that you want the UI updates to occur outside the locks or worker threads should be able to continue working without having to wait for main thread to update the UI (at least this is how I understand the requirement).
Here the locks are never during the producing of items, or updating the UI. They are held only during the short duration it takes to enqueue/dequeue item in the queue.
using System;
using System.Collections.Generic;
using System.Threading;
using System.Threading.Tasks;
using static System.Threading.Thread;
namespace ConsoleApp1
{
internal static class Program
{
private class WorkItem
{
public string SomeData { get; init; }
}
private static readonly Queue<WorkItem> s_workQueue = new Queue<WorkItem>();
private static void Worker()
{
var random = new Random();
// Simulate some work
Sleep(random.Next(1000));
// Produce work item outside the lock
var workItem = new WorkItem
{
SomeData = $"data produced from thread {CurrentThread.ManagedThreadId}"
};
// Acquire lock only for the short time needed to add the work item to the stack
lock (s_workQueue)
{
s_workQueue.Enqueue(workItem);
// Notify the main thread that a new item is added to the queue causing it to wakeup
Monitor.Pulse(s_workQueue);
}
// work item is now queued, no need to wait for main thread to finish updating the UI
// Continue work here
}
private static WorkItem GetWorkItem()
{
// Acquire lock only for the duration needed to get the item from the queue
lock (s_workQueue)
{
WorkItem result;
// Try to get the item from the queue
while (!s_workQueue.TryDequeue(out result))
{
// Lock is released during Wait call
Monitor.Wait(s_workQueue);
// Lock is acquired again after Wait call
}
return result;
}
}
private static void Main(string[] args)
{
const int totalTasks = 10;
for (var i = 0; i < totalTasks; i++)
{
_ = Task.Run(Worker);
}
var remainingTasks = totalTasks;
// Main loop (similar to message loop)
while (remainingTasks > 0)
{
var item = GetWorkItem();
// Update UI
Console.WriteLine("Got {0} and updated UI on thread {1}.", item.SomeData, CurrentThread.ManagedThreadId);
remainingTasks--;
}
Console.WriteLine("Done");
}
}
}
Update
Since you don't want to have the main thread Wait for an event, you can change the code as follows:
private static WorkItem? GetWorkItem()
{
// Acquire lock only for the duration needed to get the item from the queue
lock (s_workQueue)
{
// Try to get the item from the queue
s_workQueue.TryDequeue(out var result);
return result;
}
}
private static void Main(string[] args)
{
const int totalTasks = 10;
for (var i = 0; i < totalTasks; i++)
{
_ = Task.Run(Worker);
}
var remainingTasks = totalTasks;
// Main look (similar to message loop)
while (remainingTasks > 0)
{
var item = GetWorkItem();
if (item != null)
{
// Update UI
Console.WriteLine("Got {0} and updated UI on thread {1}.", item.SomeData, CurrentThread.ManagedThreadId);
remainingTasks--;
}
else
{
// Queue is empty, so do some other work here then try again after the work is done
// Do some other work here
// Sleep to simulate some work being done by main thread
Thread.Sleep(100);
}
}
Console.WriteLine("Done");
}
The problem in the above solution is that the Main thread should do only part of the work it is supposed to do, then call GetWorkItem to check if the queue has something, before resuming whatever it was doing again. It is doable if you can divide that work into small pieces that don't take too long.
I don't know if my answer here is what you want. What do you imagine the main thread would be doing when there are no work items in the queue?
if you think it should be doing nothing (i.e. waiting) then the Wait solution should be fine.
If you think it should be doing something, then may be that work it should be doing can be queued as a Work item as well.

Hangfire - DisableConcurrentExecution - Prevent concurrent execution if same value passed in method parameter

Hangfire DisableConcurrentExecution attribute not working as expected.
I have one method and that can be called with different Id. I want to prevent concurrent execution of method if same Id is passed.
string jobName= $"{Id} - Entry Job";
_recurringJobManager.AddOrUpdate<EntryJob>(jobName, j => j.RunAsync(Id, Null), "0 2 * * *");
My EntryJob interface having RunAsync method.
public class EntryJob: IJob
{
[DisableConcurrentExecution(3600)] <-- Tried here
public async Task RunAsync(int Id, SomeObj obj)
{
//Some coe
}
}
And interface look like this
[DisableConcurrentExecution(3600)] <-- Tried here
public interface IJob
{
[DisableConcurrentExecution(3600)] <-- Tried here
Task RunAsync(int Id, SomeObj obj);
}
Now I want to prevent RunAsync method to call multiple times if Id is same. I have tried to put DisableConcurrentExecution on top of the RunAsync method at both location inside interface declaration and also from where Interface is implemented.
But it seems like not working for me. Is there any way to prevent concurrency based on Id?
The existing implementation of DisableConcurrentExecution does not support this. It will prevent concurrent executions of the method with any args. It would be fairly simple to add support in. Note below is untested pseudo-code:
public class DisableConcurrentExecutionWithArgAttribute : JobFilterAttribute, IServerFilter
{
private readonly int _timeoutInSeconds;
private readonly int _argPos;
// add additional param to pass in which method arg you want to use for
// deduping jobs
public DisableConcurrentExecutionAttribute(int timeoutInSeconds, int argPos)
{
if (timeoutInSeconds < 0) throw new ArgumentException("Timeout argument value should be greater that zero.");
_timeoutInSeconds = timeoutInSeconds;
_argPos = argPos;
}
public void OnPerforming(PerformingContext filterContext)
{
var resource = GetResource(filterContext.BackgroundJob.Job);
var timeout = TimeSpan.FromSeconds(_timeoutInSeconds);
var distributedLock = filterContext.Connection.AcquireDistributedLock(resource, timeout);
filterContext.Items["DistributedLock"] = distributedLock;
}
public void OnPerformed(PerformedContext filterContext)
{
if (!filterContext.Items.ContainsKey("DistributedLock"))
{
throw new InvalidOperationException("Can not release a distributed lock: it was not acquired.");
}
var distributedLock = (IDisposable)filterContext.Items["DistributedLock"];
distributedLock.Dispose();
}
private static string GetResource(Job job)
{
// adjust locked resource to include the argument to make it unique
// for a given ID
return $"{job.Type.ToGenericTypeString()}.{job.Method.Name}.{job.Args[_argPos].ToString()}";
}
}

Curator LeaderLatch EOFException on shutdown

We use LeaderLatch to select leader on my cluster.
we use it like this:
leaderLatch.addListener(new LeaderLatchListener() {
#Override
public void isLeader() {
// create leader tasks runner
}
#Override
public void notLeader() {
// shutdown leader tasks runner
});
leaderLatch.start();
leaderLatch.await();
We also have a graceful shutdown process:
CloseableUtils.closeQuietly(leaderLatch);
now, the problem is when I shutdown a non-leader instance, the await() method throws a EOFException.
This is the code from LeaderLatch itself:
public void await() throws InterruptedException, EOFException
{
synchronized(this)
{
while ( (state.get() == State.STARTED) && !hasLeadership.get() )
{
wait();
}
}
if ( state.get() != State.STARTED )
{
throw new EOFException();
}
}
since I have closed it - the state is not STARTED but CLOSED so empty EOFException is thrown.
Is there a better way?
We use curator-recepies-4.2.0
Regards,
Ido
The contract for await() is to not return until it owns the lock. It has no way of indicating that you don't own the lock other than to throw an exception. I suggest you use the version of await that takes a timeout and returns a boolean. You can then close the lock and check the result of await(). Do this in a loop if you want.

Static Initialization and Use of a Class in a Separate Module in D

In my program, I have a class that I want to be allocated before entering main(). I'd like to tuck these away in a separate module to keep the clutter out of my code; However, as soon as the module goes out of scope (before main() is entered), the objects are deallocated, leaving me trying to use a null reference in main. A short example:
// main.d
import SceneData;
int main(string[] argv)
{
start.onSceneEnter();
readln();
return 0;
}
// SceneData.d
import Scene;
public
{
Scene start;
}
static this()
{
Scene start = new Scene("start", "test", "test";
}
// Scene.d
import std.stdio;
class Scene
{
public
{
this(string name)
{
this.name = name;
}
this(string name, string descriptionOnEnter, string descriptionOnConnect)
{
this.name = name;
this.descriptionOnEnter = descriptionOnEnter;
this.descriptionOnConnect = descriptionOnConnect;
}
void onSceneEnter()
{
writeln(name);
writeln(descriptionOnEnter);
}
}
private
{
string name;
string descriptionOnEnter;
string descriptionOnConnect;
}
}
I'm still getting used to the concept of modules being the basic unit of encapsulation, as opposed to the class in C++ and Java. Is this possible to do in D, or must I move my initializations to the main module?
Here:
static this()
{
Scene start = new Scene("start", "test", "test");
}
"start" is a local scope variable that shadows global one. Global one is not initialized.
After I have changed this to:
static this()
{
start = new Scene("start", "test", "test");
}
Program crashed no more.