On 30/12/2014 00:49, Kyle Lutz wrote:
On Mon, Dec 29, 2014 at 2:58 PM, Thomas M
wrote: On 29/12/2014 22:51, Kyle Lutz wrote:
On Mon, Dec 29, 2014 at 1:19 PM, Thomas M
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