|
Boost : |
Subject: Re: [boost] Synchronization (RE: [compute] review)
From: Thomas M (firespot71_at_[hidden])
Date: 2014-12-30 03:31:37
On 30/12/2014 00:49, Kyle Lutz wrote:
> On Mon, Dec 29, 2014 at 2:58 PM, Thomas M <firespot71_at_[hidden]> wrote:
>> On 29/12/2014 22:51, Kyle Lutz wrote:
>>>
>>> On Mon, Dec 29, 2014 at 1:19 PM, Thomas M <firespot71_at_[hidden]> wrote:
>>>>
>>
>>
>> Interested yes, time is currently a problem though; I'd need to familiarize
>> myself much deeper with your implementation.
>> At this stage my main concern is exception safety - how one could relief
>> users in a simplistic manner from the need to manually taking care that
>> objects do not get out-of-scope (due to an exception thrown) while an OpenCL
>> asynchronous operation still needs them. Note that because your API can
>> throw I consider exception effects to be of much greater concern than with
>> the (implicitly non-throwing) Khronos API; and just enqueuing a couple of
>> commands can make a proper, manual cleanup really easily non-trivial.
>
> I have tried as much as possible to ensure that the library
> exception-safe (I surely hope you aren't assuming that I intentionally
> made the library non-exception-safe). If you do find any bugs related
> to exception handling please do submit them with a reproducible
> test-case to the issue tracker [1] and I will get them fixed as soon
> as possible.
No, I never assumed you make it intentionally unsafe. However you, as
implementer, probably know ways more about when something may throw (and
how things then behave) then users, and thus which exception guarantees
your library implicitly provides. Is there a docs section which
explicitly says under which circumstances something may throw and how
subsequently behavior, e.g. of asynchronous operations already launched,
is defined?
I take your transform tutorial code as example:
// create a vector on the device
compute::vector<float> device_vector(host_vector.size(), context);
// transfer data from the host to the device
compute::copy(host_vector.begin(), host_vector.end(),
device_vector.begin(), queue);
// calculate the square-root of each element in-place
compute::transform(device_vector.begin(),
device_vector.end(),
device_vector.begin(),
compute::sqrt<float>(),
queue);
// copy values back to the host
compute::copy(device_vector.begin(), device_vector.end(),
host_vector.begin(), queue);
For neither the copy nor transform algorithm does the doc explicitly say
that it isn't throwing anything (apologize if it says so in another doc
section), so for exception-safe code as user I must assume the worst,
i.e. any of them can throw (for the moment assume that I don't choose to
disable throwing by #define). So I must ensure that device_vector will
be kept alive until all commands which were successfully launched could
finish; that appears to boiling down to something like a try around the
copy-transform-copy, and a queue.finish() (or similar) in catch. If
there is more than one container / command-queue etc. involved the
complexity of sorting things out for proper cleanup can raise
considerably; so does it when within the program hierarchy the
creation/lifetime management of a container on the one hand and the
command queue ownership / invocation of operations on the container on
the other hand are separated (there may simply be naturally no central
place which can control both the container lifetime and also have access
to the command queue(s)).
Note the aim is not desperately trying to get the results of the
transform somehow, just to ensure that the OpenCL runtime is not doing
anything disastrous in the asynchronous background, i.e. here accessing
memory that has already gone out of scope due to stack unwinding. If
your implementation already provides some exception / safety guarantees
in the example then these must become easily visible to users.
Thomas
Boost list run by bdawes at acm.org, gregod at cs.rpi.edu, cpdaniel at pacbell.net, john at johnmaddock.co.uk