% GPUDeviceManager Manager for GPU Devices
%    parallel.gpu.GPUDeviceManager provides events that indicate when a GPU device
%    has been selected or deselected. These events also fire when a GPU device is
%    reset.
%
%    There is only a single instance of the parallel.gpu.GPUDeviceManager available
%    in a given MATLAB session, and it is obtained using the method
%    parallel.gpu.GPUDeviceManager.instance.
%
%    parallel.gpu.GPUDeviceManager events:
%        DeviceSelected    - Fired after a GPU device is selected
%        DeviceDeselecting - Fired just before a GPU device is deselected
%
%    parallel.gpu.GPUDeviceManager properties:
%        SelectedDevice   - The currently selected GPU device
%
%    parallel.gpu.GPUDeviceManager methods:
%        getDeviceCount   - Return the number of GPU devices available
%        selectDevice     - Select a GPU device
%
%    See also GPUDEVICE, GPUDEVICECOUNT, GPUARRAY.

% Copyright 2015 The MathWorks, Inc.
classdef GPUDeviceManager < handle
    properties (Dependent)
        % The SelectedDevice property contains the currently selected GPU Device, or []
        % if no GPU device is selected.
        SelectedDevice
    end
    
    events
        % The DeviceSelected event is fired whenever a GPU Device is selected. When
        % resetting a GPU device, DeviceDeselecting and then DeviceSelected will be
        % fired in turn. The GPUDeviceEventData for a DeviceSelected event will
        % indicate the index of the newly-selected device.
        %
        % See also parallel.gpu.GPUDeviceEventData.
        DeviceSelected

        % The DeviceDeselecting event is fired just before a GPU Device is
        % deselected. When resetting a GPU device, DeviceDeselecting and then
        % DeviceSelected will be fired in turn. The GPUDeviceEventData for a
        % DeviceDeselecting event will indicate the index of the current GPU
        % device. At the point that the DeviceDeselecting event is fired,
        % existing gpuArray data is still valid.
        %
        % See also parallel.gpu.GPUDeviceEventData.
        DeviceDeselecting
    end
    
    methods
        function currentDevice = get.SelectedDevice(~)
            if parallel.internal.gpu.isAnyDeviceSelected()
                currentDevice = gpuDevice();
            else
                currentDevice = parallel.gpu.CUDADevice.empty();
            end
        end
    end
    
    methods (Static)
        function obj = instance()
        % obj = parallel.gpu.GPUDeviceManager.instance Access the GPUDeviceManager
        % instance for this MATLAB process.
            persistent OBJ
            if isempty(OBJ) || ~isvalid(OBJ)
                OBJ = parallel.gpu.GPUDeviceManager();
                % MLOCK required to keep this class in sync with C++ state.
                mlock;
            end
            obj = OBJ;
        end
    end
    methods (Access = private)
        function obj = GPUDeviceManager()          
        end
    end
    methods (Static, Hidden)
        function selected(newIdx)
            obj = parallel.gpu.GPUDeviceManager.instance();
            notify(obj, 'DeviceSelected', parallel.gpu.GPUDeviceEventData(newIdx));
        end
        function deselecting(oldIdx)
            obj = parallel.gpu.GPUDeviceManager.instance();
            notify(obj, 'DeviceDeselecting', parallel.gpu.GPUDeviceEventData(oldIdx));
        end
    end
    methods
        function count = getDeviceCount(~)
        % count = getDeviceCount(OBJ) returns the number of available GPU devices.
            count = gpuDeviceCount();
        end
        
        function device = selectDevice(~, deviceIndex)
        % device = selectDevice(OBJ, INDEX) selects the GPU device with index INDEX, and
        % returns a GPUDevice object.
            device = gpuDevice(deviceIndex);
        end
        
        function device = getDevice(~, deviceIndex)
        % device = getDevice(OBJ, INDEX) returns the GPU device with index INDEX without
        % selecting it.
            device = parallel.gpu.CUDADevice.hBuild(deviceIndex);
        end
        
        function available = isAvailable(~, indices)
        % available = isAvailable(OBJ, INDICES) Check whether a GPU device is available for use.
            available = parallel.gpu.GPUDevice.isAvailable(indices);
        end
    end
end
