|
|
Home » Developing U++ » U++ Developers corner » [PROPOSAL]: AsyncQueue class (a single threaded synchronization tool) for U++ (Discussion about implementing a single threaded synchronization tool for handling non-blocking I/O.)
[PROPOSAL]: AsyncQueue class (a single threaded synchronization tool) for U++ [message #46072] |
Sun, 28 February 2016 19:47 |
Oblivion
Messages: 1093 Registered: August 2007
|
Senior Contributor |
|
|
Hello guys,
I don't really want to flood this forum with proposals every other day. But AsyncQueue is the last class I'll share with you before I publish the SSH package.
In fact AsyncQueue is what made the SSH package for U++ possible.
I'm uploading the package here.
New version is even simple and it uses callbacks,allows lambdas.
It also contains both the API docs and an article/tutorial covering the rationale, aim, features, highlights and usage of the AsyncQueue package.
AsyncQueue is actually a generic version of non-blocking/async interface first used in HttpRequest. (Then I used the same model in NetworkProxy package, which I am now refactoring using AsyncQueue)
Excerpt from the article:
Rationale
U++ provides a rich set of core classes, including synchronisation tools and primitives designed mainly with multithreading in mind. Considering the general trends in, and demands of, modern computing, this is perfectly reasonable, if not imperative. When done properly, multithreading can and usually does give the best performance/cost ratio with a negligable overhead. Hovewer, multithreading is not always the optimal solution, and has its own disadvantages. For one, it is relatively difficult to write and debug a multithreaded code, as multithreading bring in its own set of problems such as classic concurrency problems which must be taken care of with extreme caution. Hence the increase of complexity. Moreover, multithreading models does not always scale well. Not every asynchronous operation or applicaton requires, or benefits from multithreading. Enter AsyncQueue.
Aim
AsyncQueue helper class does not aspire to provide an alternative to the existing multithreading classes in U++. Rather it is meant to be a small addition to the arsenal of synchronisation tools in the Core package, providing through a standardized interface a simple yet flexible asynchronous model targeting single-threaded component and application development, including but not limited to common socket operations, where multithreading either is not desirable or can easily get costly.
...
Please take your time to read the article.
Package has a BSD license, so feel free to use/modify it.
What is lacking? Example, of course Its example code will be SSH package, I guess...
Any suggestions, bug reports, criticism is always welcome.
Regards,
Oblivion.
Github page: https://github.com/ismail-yilmaz
upp-components: https://github.com/ismail-yilmaz/upp-components
Bobcat the terminal emulator: https://github.com/ismail-yilmaz/Bobcat
[Updated on: Wed, 02 March 2016 20:18] Report message to a moderator
|
|
|
|
|
Re: [PROPOSAL]: AsyncQueue class (a single threaded synchronization tool) for U++ [message #46076 is a reply to message #46074] |
Mon, 29 February 2016 17:23 |
|
mirek
Messages: 13976 Registered: November 2005
|
Ultimate Member |
|
|
Just a small nitpicking for now...
for(int i = 0; i < test_stack.GetCount(); i++) {
.......
test_stack.Remove(i);
this combo is generally incorrect - it skips the element after the removed one. It probably works OK here (the next element gets tested/removed in the pass), but still...
No need for if, it is OK to call unassigned Callback - it is NOP.
template<class T>T& GetJobArg(int i) { return job_queue[i].Get<T>(i); }
I smell the bug here - is 'i' index of VectorMap or index of VarArgs?
Overall, I am pretty unsure what is that VectorMap useful for. You are never using it as map,except for RemoveJob. Not that all those VectorMap::Insert/Remove operations are quite slow.
In practice, I am not quite sure you really need a queue there. Do we really need more than single level?
Also, we are going full C++11 soon. Lambdas would make superior alternative for both DoJob and VarArgs IMO.
Mirek
[Updated on: Mon, 29 February 2016 17:37] Report message to a moderator
|
|
|
Re: [PROPOSAL]: AsyncQueue class (a single threaded synchronization tool) for U++ [message #46077 is a reply to message #46076] |
Mon, 29 February 2016 18:25 |
Oblivion
Messages: 1093 Registered: August 2007
|
Senior Contributor |
|
|
Quote:Just a small nitpicking for now...
for(int i = 0; i < test_stack.GetCount(); i++) {
.......
test_stack.Remove(i);
this combo is generally incorrect - it skips the element after the removed one. It probably works OK here (the next element gets tested/removed in the pass), but still...
No problem. I'll change this one.
Quote:
I smell the bug here - is 'i' index of VectorMap or index of VarArgs?
Yes, that's a bug. Thanks!
It should be:
template<class T>T& GetJobArg(int i) { return job_queue[0].Get<T>(i); } // Implies that it is of current job's.
Quote:In practice, I am not quite sure you really need a queue there. Do we really need more than single level?
Well, if I understand you correctly, yes. I'll take my example from the SFtp class, which shows a common case I encounter.
SFtp class has several file manipulation methods (e.g. open, fstat, close).
1) If we want to retrieve info/stats of a certain file asynchronously, we can open() a file, pass its handle to fstat(), read the result, and then close() the file when our job is finished. We can do it all by ourselves. Using a loop in our source code. But doing this can , and most of the time actually do get tedious. Since it'll require extra coding, state tracking and error checking in most cases. (In SFtp class, or in the next version of my Ftps class, where non-blocking I/O is default, reading and writing data is even more complicated).
OR
2) We can simply program a async convenience method StartGetInfo() to do such operation (open() + fstat() + ... + close()) in just one step for us.
(There are use cases for both and SFtp allows both, by the way).
The bottom line is, my experience with asyncronous I/O showed me that programmable queue allows (not imposes) simplification with a very little cost.
Quote:Also, we are going full C++11 soon. Lambdas would make superior alternative for both DoJob and VarArgs IMO.
I have no objections to this. In fact lambdas are like god-send. Also, the first version of AsyncQueue actually utilized U++ callbacks (not lambda compatible ones). But I decided against it, since it resulted in other problems that DoJob() can easily solve.
But I have mainly two concerns: AFAIK, C++11 is not yet default in U++ and it would break backward compatibility.
Yet ,if you'd like me to implement it using lambdas, I'll definitely give it a go.
Regards,
Oblivion
Github page: https://github.com/ismail-yilmaz
upp-components: https://github.com/ismail-yilmaz/upp-components
Bobcat the terminal emulator: https://github.com/ismail-yilmaz/Bobcat
[Updated on: Mon, 29 February 2016 18:39] Report message to a moderator
|
|
|
|
|
|
Re: [PROPOSAL]: AsyncQueue class (a single threaded synchronization tool) for U++ [message #46085 is a reply to message #46079] |
Wed, 02 March 2016 00:22 |
Oblivion
Messages: 1093 Registered: August 2007
|
Senior Contributor |
|
|
Hello Mirek,
I've got rid of VarArgs, VectorMap, DoJob(), and re-based AsyncQueue class as a thin wrapper of vector containing callbacks.
Now it can also utilize lambda callbacks in-place (e.g. this way any StartFoo() can be both used for programming the queue, and as the place to write the actual non-blocking code.), and have backward compatibility.
While U++ callbacks allow up to 5 args, using std::tuple and std::get it can be increased by the user, if necessary.
If you approve this one, I'll rewrite the document accordingly and upload the package asap...
class AsyncQueue : Moveable<AsyncQueue> {
Vector<Callback> job_queue;
bool halted;
protected:
Callback& AddJob() { return job_queue.Add(); }
AsyncQueue& AddJob(Callback cb) { job_queue.Add(cb); return *this; }
Callback& InsertJob(int i) { return job_queue.Insert(i); }
AsyncQueue& InsertJob(int i, Callback cb) { job_queue.Insert(i, cb); return *this; }
Callback& GetJob(int i) { return job_queue.At(i); }
void RemoveJob(int i) { job_queue.Remove(i); }
void ProcessQueue() { if(!job_queue.IsEmpty()) job_queue.Remove(0); }
void ClearQueue() { if(!job_queue.IsEmpty()) job_queue.Clear(); halted = false; }
bool QueueHasJobs() const { return job_queue.GetCount() > 1; }
bool QueueIsHalted() const { return halted; }
bool QueueIsEmpty() const { return job_queue.IsEmpty(); }
bool GetJobCount() const { return job_queue.GetCount(); }
void Halt() { job_queue.Clear(); halted = true; }
public:
virtual bool Do() { if(!job_queue.IsEmpty()) GetJob(0)(); WhenDo(); return InProgress(); }
bool InProgress() const { return !job_queue.IsEmpty(); }
bool IsSuccess() const { return job_queue.IsEmpty() && !halted; }
bool IsFailure() const { return halted; }
Callback WhenDo;
AsyncQueue() : halted(false) {}
virtual ~AsyncQueue() {}
AsyncQueue(AsyncQueue rval_ a) { job_queue = pick(a.job_queue); halted = a.halted; }
void operator=(AsyncQueue rval_ a) { ClearQueue(); job_queue = pick(a.job_queue); halted = a.halted; }
};
What do you think?
Regards,
Oblivion
Github page: https://github.com/ismail-yilmaz
upp-components: https://github.com/ismail-yilmaz/upp-components
Bobcat the terminal emulator: https://github.com/ismail-yilmaz/Bobcat
[Updated on: Wed, 02 March 2016 01:01] Report message to a moderator
|
|
|
|
Goto Forum:
Current Time: Sat May 11 16:20:53 CEST 2024
Total time taken to generate the page: 0.02417 seconds
|
|
|