Overview
Examples
Screenshots
Comparisons
Applications
Download
Documentation
Tutorials
Bazaar
Status & Roadmap
FAQ
Authors & License
Forums
Funding Ultimate++
Search on this site
Search in forums












SourceForge.net Logo
Home » Developing U++ » UppHub » Job package: A lightweight worker thread for non-blocking operations. (A)
Re: RE: Job package: A scope-bound worker thread for non-blocking operations. [message #48794 is a reply to message #48793] Tue, 19 September 2017 11:12 Go to previous messageGo to next message
Oblivion is currently offline  Oblivion
Messages: 1091
Registered: August 2007
Senior Contributor
Hello Mirek,
Thanks for all the corrections. I find your opinions really valuable and useful. I was suspicios there was something wrong with that benchmark too.

Quote:
This is not quite true (unlike Job, which in fact requires you to move the data if you need it outside the Job).


Not necessarily. I can always use a pointer or Ref(), or std::ref. (In it's current state it wouldn't be as clean as capturing, I admit, but can be improved)
Or I can simply pass reference, using lambda capture. That is not forbidden. But then I have to take into account the object's life time too.
Job solves some of those head aches. (Of course, tt may introduce its own).

For the sake of discussion (ugly):
In Job.h
    template<class V = T>
    Job(V v) : Job()							{ *data = v; }



then, in Example.cpp:

	int i;
	
	Job<int*> job(&i);
	job.Start([]{ auto& n = *Job<int*>::Data() = 100;}); 
	job.Finish();
	Cout() << "Number is " << i << '\n';



Quote:
future / promise also seems to have superior error handling facilities (exceptions can easily be passed from thread to caller).



Not really hard to add. I can: process JobErrors, and re-throw plain Upp::Exc, or std::exception category.

Quote:

The one thing that seems to be missing is future -> promise abort.



Job has a per-job cancel mechanism.


So what do you suggest?

Your Async wrapper is really simple and nice. Are you going to add it to U++?

Or should I improve Job? (add exception forwarding, easier way to pass references and pointers...)

My favourite Smile : Or should I re-introduce Future/promise pair as was before? After all Job was inially a future/promise wrapper. But it lacked RAII and worker thead model.
Now they are in place. All I need to do is to incorporate it to the current model using the Async example you provided (except cowork).
(Without changing the public api of Job much. IMO It looks familiar and acts fine.)

Or should I simply abandon it? This tool is born out of need, which plain future/promise mechanism couldn't satisfy (i.e. a fine-grained control over threads for non-blocking behaviour.)

I don't see a reson to hang onto it if there will be a better solution.

Again, thank you very much for taking time to discuss the issue.

Job currently lacks shared states but it is not what Job is meant for anyway. IMO CoWork is there for it (and for bunch of other things).

Best regards,
Oblivion


[Updated on: Tue, 19 September 2017 11:37]

Report message to a moderator

Re: RE: Job package: A scope-bound worker thread for non-blocking operations. [message #48795 is a reply to message #48793] Tue, 19 September 2017 12:31 Go to previous messageGo to next message
Oblivion is currently offline  Oblivion
Messages: 1091
Registered: August 2007
Senior Contributor
How about adding An Async/Result pair, the way you suggested.

ASync will be an improved version of the example you provided with (using CoWork), addition of result id generator (for cancellation), exception handling:
Result will be a wrapper for future where we can do such things as follows:


      auto result1 = Async([=]{ return GetDivisors(); })
      while(!result.IsReady())
             ; // Do something;
      Cout() << result.IsValid() ? result.Get() : result.GetErrorDesc();

      // and of course...
      Cout() << Async([=]{ return "Hello World\n"; }).Get();
             





Result's Interface may be consisted of:
Result::Get();

Result::IsReady();

Result::IsValid();

Result::GetError();

Result::GetErrorDesc():

Result::GetId();

Result::Cancel();




If you'd like, I can implement it this way. (IMO Result is a more meaningful name, but naming can change...)

Best regards,
Oblivion


[Updated on: Tue, 19 September 2017 12:35]

Report message to a moderator

Re: RE: Job package: A scope-bound worker thread for non-blocking operations. [message #48796 is a reply to message #48795] Tue, 19 September 2017 13:01 Go to previous messageGo to next message
mirek is currently offline  mirek
Messages: 13975
Registered: November 2005
Ultimate Member
Oblivion wrote on Tue, 19 September 2017 12:31
How about adding An Async/Result pair, the way you suggested.

ASync will be an improved version of the example you provided with (using CoWork), addition of result id generator (for cancellation), exception handling:
Result will be a wrapper for future where we can do such things as follows:


      auto result1 = Async([=]{ return GetDivisors(); })
      while(!result.IsReady())
             ; // Do something;
      Cout() << result.IsValid() ? result.Get() : result.GetErrorDesc();

      // and of course...
      Cout() << Async([=]{ return "Hello World\n"; }).Get();
             





Result's Interface may be consisted of:
Result::Get();

Result::IsReady();

Result::IsValid();

Result::GetError();

Result::GetErrorDesc():

Result::GetId();

Result::Cancel();




If you'd like, I can implement it this way. (IMO Result is a more meaningful name, but naming can change...)

Best regards,
Oblivion


Not sure what is best...

Mirek
Re: RE: Job package: A scope-bound worker thread for non-blocking operations. [message #48807 is a reply to message #48796] Sat, 23 September 2017 14:17 Go to previous messageGo to next message
Oblivion is currently offline  Oblivion
Messages: 1091
Registered: August 2007
Senior Contributor
Hello Mirek (and Upp community),


Job package is updated in accordance with the above discussion.

To sum up, it is heavily improved (tested on GCC 7.2.0, MinGW (supplied with U++), and latest MinGWx64, and MSC 2017):

- Void type instantiation is re-introduced for good (does not rely on anything other than plain template specializaton rules.)

- Value return semantics is re-introduced for good.

- Constant reference access operator is added. (This is especially useful when using Job with containers such as Vector, or array (e.g. Job<Vector<int>>) in a loop. See JobExample test app provided)

- Exception propagation mechanism is added. Workers will propagate raised exceptions to their caller. (when one of the GeResult(), operator~(), or IsError() methods is called. )

- JobExample test application is added to the package. This example demonstrates the above mentioned traits.
  


I believe with the recent update Job is now slightly superior to async/future/promise trio (not shared_future), in that it has full control over its thread (which is a proper worker thread) worker cancelleation/abortion mechanics,
and optional container-like reference access to its result which can reduce copying/moving where unnecessary. (Except Job doesn'T support reference specialization. Job<T&> is not possible for now. But that is not a big deal, really) Smile


Bug reports, criticism and feedback is deeply appreciated.


[ See below message for new version ]

Best regards.
Oblivion


[Updated on: Sat, 07 October 2017 14:55]

Report message to a moderator

Re: RE: Job package: A scope-bound worker thread for non-blocking operations. [message #48829 is a reply to message #48796] Sat, 07 October 2017 15:03 Go to previous messageGo to next message
Oblivion is currently offline  Oblivion
Messages: 1091
Registered: August 2007
Senior Contributor
Hello,

Job package is now compatible with single-threaded U++ environment. (Yet -almost- all methods and global functions retain their functionality under ST env.)
Documentation and provided example is updated accordingly.


The code below is a part of JobExample.cpp and demonstrates both non-blocking behaviour and Job cancelling feature in a single-threaded environment:

void CancelJob()
{
	// Singlethreaded version. (non-blocking operation)
	// Below example is one of the simplest ways to achive non-blocking operations with Job in a
	// single-threaded environment. A finer-grained operation would involve handling of return
	// codes. (e.g. using Job<int>)
	
	Job<void> job;
	auto work = [=] {
		if(IsJobCancelled()) {
			Cout() << "Worker #" << GetWorkerId() << " received cancel signal. Cancelling job...\n";
			throw JobError("Operation cancelled.");
		}
	};
	
	Cout() << "Worker #" << GetWorkerId() << " started. (Waiting the cancellation signal...)\n";
	const int timeout = 5000;
	auto start_time = msecs();
	while(!job.IsError()) {
		job.Start(work);
		if(msecs(start_time) >= timeout)
			job.Cancel();	// Or if you have more than one non-blocking operation in progress,
		                  	// you can call CancelJobs() global function to cancel all running jobs.
	}
}



As always, reviews, criticism, bug reports, etc. are deeply appreciated.

Best regards,
Oblivion




[Updated on: Sat, 07 October 2017 15:09]

Report message to a moderator

Re: RE: Job package: A scope-bound worker thread for non-blocking operations. [message #48830 is a reply to message #48829] Sat, 07 October 2017 15:51 Go to previous messageGo to next message
mirek is currently offline  mirek
Messages: 13975
Registered: November 2005
Ultimate Member
Oblivion wrote on Sat, 07 October 2017 15:03
Hello,

Job package is now compatible with single-threaded U++ environment. (Yet -almost- all methods and global functions retain their functionality under ST env.)


ST is now deprecated...

BTW, have you noticed that, inspired by this discussion thread, CoWork now has cancelation and exception propagation?

IMO, that makes it very similar to job, the only part that is really missing is the return value.

Actually, the main reason I have not implemented that yet is that I do not like the explicit specification of return value, as in Job. Which requires to implement it as function, which in turn requires some form of 'future'.

Mirek
Re: RE: Job package: A scope-bound worker thread for non-blocking operations. [message #48831 is a reply to message #48830] Sat, 07 October 2017 16:04 Go to previous messageGo to next message
Oblivion is currently offline  Oblivion
Messages: 1091
Registered: August 2007
Senior Contributor
Hello Mirek,


Quote:
ST is now deprecated...


Sure, The reason I added ST support is that I have some code that needs to be backward (ST) compatible. And for those who want't to keep their code as such. (It wasn't very difficult to add, anyway).

Quote:

BTW, have you noticed that, inspired by this discussion thread, CoWork now has cancelation and exception propagation?

IMO, that makes it very similar to job, the only part that is really missing is the return value.

Actually, the main reason I have not implemented that yet is that I do not like the explicit specification of return value, as in Job. Which requires to implement it as function, which in turn requires some form of 'future'.

Mirek


No, I didn't notice it till now, that's great, one less reason for me to use Job, thanks! Smile
I'll try and see if I can use CoWork instead. (It shouldn't be very difficult)
Job's return semantics proved really useful, so for the time being I intend to maintain it.

And in the meantime let's see if I can come up with a suitable alternative to std::future (to use it with CoWork).

Best regards,
Oblivion.


[Updated on: Sat, 07 October 2017 16:33]

Report message to a moderator

Re: RE: Job package: A scope-bound worker thread for non-blocking operations. [message #48832 is a reply to message #48830] Sun, 08 October 2017 15:01 Go to previous messageGo to next message
Oblivion is currently offline  Oblivion
Messages: 1091
Registered: August 2007
Senior Contributor
Hello Mirek,

I wrote a prototype for U++ implementation of promise/future mechanism. It is 104 LOCs, and it took an hour for me to to design and write it (It is in its current state not perfect but it works.)

It is consisted of three classes (please do not dwell on naming, I didn't think them through, they'll change):

- WorkEntry -> std:promise   (This class is not explicitly called. It is allocated on the heap (using new), and its ownership is passed to WorkResult.)
- WorkResult -> std: future  (With pick semantics, void type specialization, error handling, exception propagation, and optional constant reference access to the result.)
- Work                       (Contains a CoWork instance, and exposes Do() and Finish() methods.)



I re-implemented your Async() code in Work class, using CoWork::Do() instead of CoWork::Schedule(). The rationale behind this decision is that sometimes asynchronous operations need to be externally blocked, all at once ("wait for all works to finish"). And I found no other reliable way. Also, works use a single semaphore to wait and run.

Example code:

//	Error handling, picking/moving.
	auto r1 = work.Do([=] { throw Work::Error("Worker failed."); });
	work.Finish();
	auto r2 = pick(r1); // r1 is picked.
	if(r2.IsError()) {
		Cout() << r2.GetErrorDesc() << '\n';
	}


There is room for improvements, and probably some issues I didn't encounter yet or I overlooked. Feel free to comment on it. (One area that needs improving and to be made more error resistant is moving/picking behaviour. I'll improve them)

Below you can find the Work, and WorkExample, which is basically JobBencmark adapted to Work.


Best regards,
Oblivion
  • Attachment: Work.zip
    (Size: 3.19KB, Downloaded 237 times)


[Updated on: Sun, 08 October 2017 15:07]

Report message to a moderator

Re: RE: Job package: A scope-bound worker thread for non-blocking operations. [message #48835 is a reply to message #48830] Tue, 10 October 2017 11:51 Go to previous messageGo to next message
Oblivion is currently offline  Oblivion
Messages: 1091
Registered: August 2007
Senior Contributor
A further refinement would be to explicity relate CoWork to promise/future pattern:

My suggested namings:
-
- WorkResult (former WorkEntry): Unlike std::promise, this is called privately. I don't really see any use in calling it explicitly
- Work (former WorkResult):     This can be the U++ std::future counterpart (see the first prototype packageI provided above) This can be a helper class to CoWork, representing a single, isolated (semantically) thread.

- CoWork::Work():               This is the thread-starter method which will return, well, Work :)



I propose adding CoWork::Work() as a (non-static) method to CoWork, because from my experience with Job,Thread, future/promise and CoWork, and as I noted on my previous message, keeping workers contained in CoWork instances, using a CoWork::Do() call, have some advantages over using a static method such as CoWork::Schedule(): Such as the ability to use CoWork::Finish() on demand, and waiting the workers to be finished automatically on class instance destruction.

As for the error management mechanism using a specific exception type such as Work::Error, along with IsError(), GetError() and GetErrorDesc() methods, IMO they would be a very useful addition, but they are not necessary, can be removed.

What do you think?

Best regards,
Oblivion



[Updated on: Tue, 10 October 2017 12:01]

Report message to a moderator

Re: RE: Job package: A scope-bound worker thread for non-blocking operations. [message #48836 is a reply to message #48835] Tue, 10 October 2017 16:10 Go to previous messageGo to next message
mirek is currently offline  mirek
Messages: 13975
Registered: November 2005
Ultimate Member
My try.

#include <Core/Core.h>

using namespace Upp;

template <class Ret>
class AsyncWork {
	template <class Ret>
	struct Imp {
		CoWork co;
		Ret    ret;
	
		template< class Function, class... Args>
		void       Do(Function&& f, Args&&... args) { co.Do([=]() { ret = f(args...); }); }
		const Ret& Get()                            { return ret; }
	};

	template <>
	struct Imp<void> {
		CoWork co;
	
		template< class Function, class... Args>
		void       Do(Function&& f, Args&&... args) { co.Do([=]() { f(args...); }); }
		void       Get()                            {}
	};
	
	One<Imp<Ret>> imp;
	
public:
	template< class Function, class... Args>
	void  Do(Function&& f, Args&&... args)    { imp.Create().Do(f, args...); }

	void  Cancel()                            { if(imp) imp->co.Cancel(); }
	Ret   Get()                               { ASSERT(imp); imp->co.Finish(); return imp->Get(); }
	Ret   operator~()                         { return Get(); }
	
	AsyncWork(AsyncWork&&) = default;
	AsyncWork()                               {}
	~AsyncWork()                              { if(imp) imp->co.Cancel(); }
};

template< class Function, class... Args>
AsyncWork<std::result_of_t<std::decay_t<Function>(std::decay_t<Args>...)>>
Async(Function&& f, Args&&... args)
{
	AsyncWork<std::result_of_t<std::decay_t<Function>(std::decay_t<Args>...)>> h;
	h.Do(f, args...);
	return pick(h);
}

CONSOLE_APP_MAIN
{
	auto h = Async([] { return "Hello world"; });
	DDUMP(h.Get());
	
	DDUMP(~Async([](int x) { return x * x; }, 9));
	
	auto x = Async([] { throw "error"; });
	try {
		x.Get();
	}
	catch(...) {
		DLOG("Exception caught");
	}
}


I guess it is quite similar now.

[Updated on: Tue, 10 October 2017 16:17]

Report message to a moderator

Re: RE: Job package: A scope-bound worker thread for non-blocking operations. [message #48837 is a reply to message #48836] Tue, 10 October 2017 17:02 Go to previous messageGo to next message
Oblivion is currently offline  Oblivion
Messages: 1091
Registered: August 2007
Senior Contributor
Hello Mirek,

I haven't tested it yet, but as usual, yours is much more clean, and concise. Smile

IMO the only missing part here is an IsFinished() method. A non-blocking wait is in some cases a must. std::future provides this with future::wait_for(),
As far as I can see it usually translates to IsFinished() in U++ terminology

bool IsFinished() { ASSERT(imp); return imp->co.IsFinished(); }




Other than that, JMO AsyncWork and Async would be a worthy addition to U++.

Best regards,


[Updated on: Tue, 10 October 2017 17:08]

Report message to a moderator

Re: RE: Job package: A scope-bound worker thread for non-blocking operations. [message #48839 is a reply to message #48837] Tue, 10 October 2017 20:51 Go to previous messageGo to next message
mirek is currently offline  mirek
Messages: 13975
Registered: November 2005
Ultimate Member
There is a tricky catch with IsFinished:

template <class Range>
ValueTypeOf<Range> ASum(const Range& rng, const ValueTypeOf<Range>& zero)
{
	int n = rng.GetCount();
	if(n == 1)
		return rng[0];
	if(n == 0)
		return 0;
	auto l = Async([&] { return ASum(SubRange(rng, 0, n / 2)); });
	auto r = Async([&] { return ASum(SubRange(rng, n / 2, n - n / 2)); });
	while(!l.IsFinished() || !r.IsFinished())
		Sleep(1);
	return l.Get() + r.Get();
}


What do you think is wrong with this code? Smile

Mirek
Re: RE: Job package: A scope-bound worker thread for non-blocking operations. [message #48840 is a reply to message #48839] Tue, 10 October 2017 23:48 Go to previous messageGo to next message
Oblivion is currently offline  Oblivion
Messages: 1091
Registered: August 2007
Senior Contributor
Hello Mirek,

tested Async/AsyncWork with MSC 2017, MinGW on windows, and with GCC on linux.

- It compiles on MSC without any hiccup.
- It does not compile on GCC (7.2) or MinGW unless the nested classes are moved out, (That's why I wrote my prototype that way.) and "Ret" is changed to some other parameter name. Here are the error codes I get:

\AsyncTest.cpp (8): error: declaration of template parameter 'Ret' shadows template parameter
\AsyncTest.cpp (18): error: explicit specialization in non-namespace scope 'class AsyncWork<Ret>'
\AsyncTest.cpp (19): error: template parameters not deducible in partial specialization:
\AsyncTest.cpp (31): error: too many template-parameter-lists
\AsyncTest.cpp (47): error: 'class AsyncWork<const char*>' has no member named 'Do' (): h.Do(f, args...);
\AsyncTest.cpp: In instantiation of 'AsyncWork<typename std::result_of<typename std::decay<_Tp>::type(std::decay_t<Args>...)>::type> Async(Function&&, Args&& ...) [wit
h Function = ConsoleMainFn_()::<lambda(int)>; Args = {int}; typename std::result_of<typename std::decay<_Tp>::type(std::decay_t<Args>...)>::type = int]':
\AsyncTest.cpp (56): required from here
\AsyncTest.cpp (47): error: 'class AsyncWork<int>' has no member named 'Do'
\AsyncTest.cpp (47): error: 'class AsyncWork<void>' has no member named 'Do'
\AsyncTest.cpp (11): error: 'AsyncWork<Ret>::Imp<Ret>::ret' has incomplete type
\AsyncTest.cpp (11): error: invalid use of 'void'
\AsyncTest.cpp (15): error: forming reference to void
\AsyncTest.cpp (34): error: 'struct AsyncWork<void>::Imp<void>' has no member named 'Get'; did you mean 'ret'?
\AsyncTest.cpp (34): error: return-statement with a value, in function returning 'void' [-fpermissive]


- More importantly there seems to be something wrong with the exception propagation mechanism. For,

1) Sometimes it fails to catch the exception, and the application crashes with that exception.
2) When it catches the exception the application hangs at the end (after the "exception caught" message is printed.)
3) Sometimes the application simply hangs.

I got this erratic behaviour both on windows and on linux, on a single machine, so it maybe a local hardware problem, I need to investigate it further...



Quote:
s a tricky catch with IsFinished:

template <class Range>
ValueTypeOf<Range> ASum(const Range& rng, const ValueTypeOf<Range>& zero)
{
int n = rng.GetCount();
if(n == 1)
return rng[0];
if(n == 0)
return 0;
auto l = Async([&] { return ASum(SubRange(rng, 0, n / 2)); });
auto r = Async([&] { return ASum(SubRange(rng, n / 2, n - n / 2)); });
while(!l.IsFinished() || !r.IsFinished())
Sleep(1);
return l.Get() + r.Get();
}



What do you think is wrong with this code? Smile

Mirek


Sure, but can this really be attributed to a design flaw?
I mean, f I'm not really missing anything else, it seems that here we simply have a careless programming.
Recursion is potentially tricky by nature, and requires the developer to be extra cautious with his/her assumptions.

A proper use of IsFinished() can be found in my JobBenchmark example , where it is simply used to check the worker, and move on to others if the job is not finished... (at least, that's what I have in my mind in the first place)

Best regards.
Oblivion


[Updated on: Wed, 11 October 2017 08:44]

Report message to a moderator

Re: RE: Job package: A scope-bound worker thread for non-blocking operations. [message #48841 is a reply to message #48840] Wed, 11 October 2017 09:23 Go to previous messageGo to next message
mirek is currently offline  mirek
Messages: 13975
Registered: November 2005
Ultimate Member
Oblivion wrote on Tue, 10 October 2017 23:48
Hello Mirek,

tested Async/AsyncWork with MSC 2017, MinGW on windows, and with GCC on linux.

- It compiles on MSC without any hiccup.
- It does not compile on GCC (7.2) or MinGW unless the nested classes are moved out, (That's why I wrote my prototype that way.) and "Ret" is changed to some other parameter name. Here are the error codes I get:


After a bit of wrestling with C++11, it now compiles.

Quote:

- More importantly there seems to be something wrong with the exception propagation mechanism. For,

1) Sometimes it fails to catch the exception, and the application crashes with that exception.
2) When it catches the exception the application hangs at the end (after the "exception caught" message is printed.)
3) Sometimes the application simply hangs.

I got this erratic behaviour both on windows and on linux, on a single machine, so it maybe a local hardware problem, I need to investigate it further...


I would like to investigate, but need a testcase.

Quote:

Sure, but can this really be attributed to a design flaw?
Recursion is potentially tricky by nature, and requires the developer to be extra cautious with his/her assumptions.


Well, recursion aside, I think that in general, we want the mechanism reentrant. I mean, it should not be a part of contract/function documentation whether is it using IsFinished internally.

I can actually fix the issue, but then IsFinished will not be doing the same thing.

BTW, the very same issue is true for CoWork. So something to think about..

Mirek
Re: RE: Job package: A scope-bound worker thread for non-blocking operations. [message #48842 is a reply to message #48841] Wed, 11 October 2017 09:42 Go to previous messageGo to next message
Oblivion is currently offline  Oblivion
Messages: 1091
Registered: August 2007
Senior Contributor
Quote:

Well, recursion aside, I think that in general, we want the mechanism reentrant. I mean, it should not be a part of contract/function documentation whether is it using IsFinished internally.


Ah, I see. Apparently I'm still not thinking out of my Job-mindset (It was theoretically reentrant, although I did not actually test it.).
CoWork is a different beast internally. I forgot that.

As for the erratic exception handling, I didn't use any code other than you provided above. Simply put, it sometimes works and sometimes doesn't. I didn't notice any regular pattern.

Best regards,
Oblivion


[Updated on: Wed, 11 October 2017 09:44]

Report message to a moderator

Re: RE: Job package: A scope-bound worker thread for non-blocking operations. [message #48843 is a reply to message #48842] Wed, 11 October 2017 09:47 Go to previous messageGo to next message
mirek is currently offline  mirek
Messages: 13975
Registered: November 2005
Ultimate Member
Oblivion wrote on Wed, 11 October 2017 09:42
Quote:

Well, recursion aside, I think that in general, we want the mechanism reentrant. I mean, it should not be a part of contract/function documentation whether is it using IsFinished internally.


As for the erratic exception handling, I didn't use any code other than you provided above. Simply put, it sometimes works and sometimes doesn't. I didn't notice any regular pattern.



Now I am confused. There is no 'throw' in my ASum example...
Re: RE: Job package: A scope-bound worker thread for non-blocking operations. [message #48844 is a reply to message #48843] Wed, 11 October 2017 09:50 Go to previous messageGo to next message
Oblivion is currently offline  Oblivion
Messages: 1091
Registered: August 2007
Senior Contributor
Quote:

Now I am confused. There is no 'throw' in my ASum example...


No, not that one. The basic test case above, withthe AsyncWork code above, where you throw "error":
CONSOLE_APP_MAIN
{
	auto h = Async([] { return "Hello world"; });
	DDUMP(h.Get());
	
	DDUMP(~Async([](int x) { return x * x; }, 9));
	
	auto x = Async([] { throw "error"; });
	try {
		x.Get();
	}
	catch(...) {
		DLOG("Exception caught");
	}
}



Re: RE: Job package: A scope-bound worker thread for non-blocking operations. [message #48846 is a reply to message #48844] Wed, 11 October 2017 19:30 Go to previous messageGo to next message
mirek is currently offline  mirek
Messages: 13975
Registered: November 2005
Ultimate Member
Weird. The only reason for erratic behaviour would be some form of race condition, so I have tried this:

#include <Core/Core.h>

using namespace Upp;

Atomic h;

void Wait(int c)
{
	while(c--)
		h++;
}

CONSOLE_APP_MAIN
{
	StdLogSetup(LOG_COUT|LOG_FILE);

	for(int i = 0; i < 100000; i++) {
		if(i % 1000 == 0)
			LOG(i);
		bool b = false;
		auto x = Async([] { Wait(Random(3000)); throw "error"; });
		try {
			Wait(Random(3000));
			x.Get();
		}
		catch(...) {
			b = true;
		}
		ASSERT(b);
	}
	
	LOG("============ OK");
}


... and it passed without a problem. Added that test to autotest just to be sure...

Anyway, (maybe I forgot to mention to AsyncWork is now in U++): Are you trying with current trunk?
Re: RE: Job package: A scope-bound worker thread for non-blocking operations. [message #48847 is a reply to message #48846] Wed, 11 October 2017 20:14 Go to previous message
Oblivion is currently offline  Oblivion
Messages: 1091
Registered: August 2007
Senior Contributor
Hello Mirek,

Quote:
Anyway, (maybe I forgot to mention to AsyncWork is now in U++): Are you trying with current trunk?


This is great news! Thank you very much for your patience and efforts. This is a really handy tool which obsoletes Job. Smile

As for the weird error I get, it seems to be stopped after I purged UPPOUT.

IF I encounter it again, I'll report it.

By the way, I noticed that there are missing topic titles in Core/src.tpp : (Huge, Function, Unicode Support). Also, Huge class title is missing in it's doc.

Best regards.
Oblivion


[Updated on: Wed, 11 October 2017 20:53]

Report message to a moderator

Previous Topic: Added SysExec package
Next Topic: firebird: CharSet
Goto Forum:
  


Current Time: Thu Mar 28 15:34:44 CET 2024

Total time taken to generate the page: 0.01652 seconds