<commit_before>#include "detection.h"
#include "deviceList.h"

#include <CoreFoundation/CoreFoundation.h>

#include <IOKit/IOKitLib.h>
#include <IOKit/IOMessage.h>
#include <IOKit/IOCFPlugIn.h>
#include <IOKit/usb/IOUSBLib.h>

#include <sys/param.h>
#include <pthread.h>

#include <uv.h>

typedef struct DeviceListItem
{
    io_object_t             notification;
    IOUSBDeviceInterface**  deviceInterface;
    DeviceItem_t*           deviceItem;
} stDeviceListItem;

static IONotificationPortRef    gNotifyPort;
static io_iterator_t            gAddedIter;
static CFRunLoopRef             gRunLoop;

CFMutableDictionaryRef  matchingDict;
CFRunLoopSourceRef      runLoopSource;

static pthread_t lookupThread;

pthread_mutex_t notify_mutex;
pthread_cond_t notifyNewDevice;
pthread_cond_t notifyDeviceHandled;

bool newDeviceAvailable = false;
bool deviceHandled = true;

ListResultItem_t* notify_item;
bool isAdded = false;
bool isRunning = false;
bool intialDeviceImport = true;

void WaitForDeviceHandled();
void SignalDeviceHandled();
void WaitForNewDevice();
void SignalDeviceAvailable();

//================================================================================================
//
//  DeviceRemoved
//
//  This routine will get called whenever any kIOGeneralInterest notification happens.  We are
//  interested in the kIOMessageServiceIsTerminated message so that's what we look for.  Other
//  messages are defined in IOMessage.h.
//
//================================================================================================
void DeviceRemoved(void *refCon, io_service_t service, natural_t messageType, void *messageArgument)
{
    kern_return_t   kr;
    stDeviceListItem* deviceListItem = (stDeviceListItem *) refCon;
    DeviceItem_t* deviceItem = deviceListItem->deviceItem;

    if (messageType == kIOMessageServiceIsTerminated)
    {
        if (deviceListItem->deviceInterface)
        {
            kr = (*deviceListItem->deviceInterface)->Release(deviceListItem->deviceInterface);
        }

        kr = IOObjectRelease(deviceListItem->notification);

        ListResultItem_t* item = NULL;
        if(deviceItem)
        {
            item = CopyElement(&deviceItem->deviceParams);
            RemoveItemFromList(deviceItem);
            delete deviceItem;
        }
        else
        {
            item = new ListResultItem_t();
        }

        WaitForDeviceHandled();
        notify_item = item;
        isAdded = false;
        SignalDeviceAvailable();

    }
}

//================================================================================================
//
//  DeviceAdded
//
//  This routine is the callback for our IOServiceAddMatchingNotification.  When we get called
//  we will look at all the devices that were added and we will:
//
//  1.  Create some private data to relate to each device (in this case we use the service's name
//      and the location ID of the device
//  2.  Submit an IOServiceAddInterestNotification of type kIOGeneralInterest for this device,
//      using the refCon field to store a pointer to our private data.  When we get called with
//      this interest notification, we can grab the refCon and access our private data.
//
//================================================================================================
void DeviceAdded(void *refCon, io_iterator_t iterator)
{
    kern_return_t       kr;
    io_service_t        usbDevice;
    IOCFPlugInInterface **plugInInterface = NULL;
    SInt32              score;
    HRESULT             res;

    while ((usbDevice = IOIteratorNext(iterator)))
    {
        io_name_t       deviceName;
        CFStringRef     deviceNameAsCFString;
        UInt32          locationID;
        UInt16          vendorId;
        UInt16          productId;
        UInt16          addr;

        DeviceItem_t* deviceItem = new DeviceItem_t();

        // Get the USB device's name.
        kr = IORegistryEntryGetName(usbDevice, deviceName);
        if (KERN_SUCCESS != kr)
        {
            deviceName[0] = '\0';
        }

        deviceNameAsCFString = CFStringCreateWithCString(kCFAllocatorDefault, deviceName, kCFStringEncodingASCII);

        if (deviceNameAsCFString)
        {
            Boolean result;
            char    deviceName[MAXPATHLEN];

            // Convert from a CFString to a C (NUL-terminated)
            result = CFStringGetCString(deviceNameAsCFString,
                                        deviceName,
                                        sizeof(deviceName),
                                        kCFStringEncodingUTF8);

            if (result)
            {
                deviceItem->deviceParams.deviceName = deviceName;
            }

            CFRelease(deviceNameAsCFString);
        }

        CFStringRef manufacturerAsCFString = (CFStringRef) IORegistryEntrySearchCFProperty(usbDevice,
                                                                                kIOServicePlane,
                                                                                CFSTR(kUSBVendorString),
                                                                                kCFAllocatorDefault,
                                                                                kIORegistryIterateRecursively);

        if (manufacturerAsCFString)
        {
            Boolean result;
            char    manufacturer[MAXPATHLEN];

            // Convert from a CFString to a C (NUL-terminated)
            result = CFStringGetCString(manufacturerAsCFString,
                                        manufacturer,
                                        sizeof(manufacturer),
                                        kCFStringEncodingUTF8);

            if (result)
            {
                deviceItem->deviceParams.manufacturer = manufacturer;
            }

            CFRelease(manufacturerAsCFString);
        }

        CFStringRef serialNumberAsCFString = (CFStringRef) IORegistryEntrySearchCFProperty(usbDevice,
                                                                                kIOServicePlane,
                                                                                CFSTR(kUSBSerialNumberString),
                                                                                kCFAllocatorDefault,
                                                                                kIORegistryIterateRecursively);

        if (serialNumberAsCFString)
        {
            Boolean result;
            char    serialNumber[MAXPATHLEN];

            // Convert from a CFString to a C (NUL-terminated)
            result = CFStringGetCString(serialNumberAsCFString,
                                        serialNumber,
                                        sizeof(serialNumber),
                                        kCFStringEncodingUTF8);

            if (result)
            {
                deviceItem->deviceParams.serialNumber = serialNumber;
            }

            CFRelease(serialNumberAsCFString);
        }

        // Now, get the locationID of this device. In order to do this, we need to create an IOUSBDeviceInterface
        // for our device. This will create the necessary connections between our userland application and the
        // kernel object for the USB Device.
        kr = IOCreatePlugInInterfaceForService(usbDevice, kIOUSBDeviceUserClientTypeID, kIOCFPlugInInterfaceID, &plugInInterface, &score);

        if ((kIOReturnSuccess != kr) || !plugInInterface)
        {
            fprintf(stderr, "IOCreatePlugInInterfaceForService returned 0x%08x.\n", kr);
            continue;
        }

        stDeviceListItem *deviceListItem = new stDeviceListItem();

        // Use the plugin interface to retrieve the device interface.
        res = (*plugInInterface)->QueryInterface(plugInInterface, CFUUIDGetUUIDBytes(kIOUSBDeviceInterfaceID), (LPVOID*) &deviceListItem->deviceInterface);

        // Now done with the plugin interface.
        (*plugInInterface)->Release(plugInInterface);

        if (res || deviceListItem->deviceInterface == NULL)
        {
            fprintf(stderr, "QueryInterface returned %d.\n", (int) res);
            continue;
        }

        // Now that we have the IOUSBDeviceInterface, we can call the routines in IOUSBLib.h.
        // In this case, fetch the locationID. The locationID uniquely identifies the device
        // and will remain the same, even across reboots, so long as the bus topology doesn't change.

        kr = (*deviceListItem->deviceInterface)->GetLocationID(deviceListItem->deviceInterface, &locationID);
        if (KERN_SUCCESS != kr)
        {
            fprintf(stderr, "GetLocationID returned 0x%08x.\n", kr);
            continue;
        }
        deviceItem->deviceParams.locationId = locationID;

        kr = (*deviceListItem->deviceInterface)->GetDeviceAddress(deviceListItem->deviceInterface, &addr);
        if (KERN_SUCCESS != kr)
        {
            fprintf(stderr, "GetDeviceAddress returned 0x%08x.\n", kr);
            continue;
        }
        deviceItem->deviceParams.deviceAddress = addr;

        kr = (*deviceListItem->deviceInterface)->GetDeviceVendor(deviceListItem->deviceInterface, &vendorId);
        if (KERN_SUCCESS != kr)
        {
            fprintf(stderr, "GetDeviceVendor returned 0x%08x.\n", kr);
            continue;
        }
        deviceItem->deviceParams.vendorId = vendorId;

        kr = (*deviceListItem->deviceInterface)->GetDeviceProduct(deviceListItem->deviceInterface, &productId);
        if (KERN_SUCCESS != kr)
        {
            fprintf(stderr, "GetDeviceProduct returned 0x%08x.\n", kr);
            continue;
        }
        deviceItem->deviceParams.productId = productId;

        // Extract path name as unique key
        io_string_t pathName;
        IORegistryEntryGetPath(usbDevice, kIOServicePlane, pathName);
        deviceNameAsCFString = CFStringCreateWithCString(kCFAllocatorDefault, pathName, kCFStringEncodingASCII);
        char cPathName[MAXPATHLEN];

        if (deviceNameAsCFString)
        {
            Boolean result;

            // Convert from a CFString to a C (NUL-terminated)
            result = CFStringGetCString(deviceNameAsCFString,
                                        cPathName,
                                        sizeof(cPathName),
                                        kCFStringEncodingUTF8);

            CFRelease(deviceNameAsCFString);
        }

        AddItemToList(cPathName, deviceItem);
        deviceListItem->deviceItem = deviceItem;

        if(intialDeviceImport == false)
        {
            WaitForDeviceHandled();
            notify_item = &deviceItem->deviceParams;
            isAdded = true;
            SignalDeviceAvailable();
        }

        // Register for an interest notification of this device being removed. Use a reference to our
        // private data as the refCon which will be passed to the notification callback.
        kr = IOServiceAddInterestNotification(gNotifyPort,                      // notifyPort
                                              usbDevice,                        // service
                                              kIOGeneralInterest,               // interestType
                                              DeviceRemoved,                    // callback
                                              deviceListItem,                   // refCon
                                              &(deviceListItem->notification)   // notification
                                              );

        if (KERN_SUCCESS != kr)
        {
            printf("IOServiceAddInterestNotification returned 0x%08x.\n", kr);
        }

        // Done with this USB device; release the reference added by IOIteratorNext
        kr = IOObjectRelease(usbDevice);
    }
}

void WaitForDeviceHandled()
{
    pthread_mutex_lock(&notify_mutex);
    if(deviceHandled == false)
    {
        pthread_cond_wait(&notifyDeviceHandled, &notify_mutex);
    }
    deviceHandled = false;
    pthread_mutex_unlock(&notify_mutex);
}

void SignalDeviceHandled()
{
    pthread_mutex_lock(&notify_mutex);
    deviceHandled = true;
    pthread_cond_signal(&notifyDeviceHandled);
    pthread_mutex_unlock(&notify_mutex);
}

void WaitForNewDevice()
{
    pthread_mutex_lock(&notify_mutex);
    if(newDeviceAvailable == false)
    {
        pthread_cond_wait(&notifyNewDevice, &notify_mutex);
    }
    newDeviceAvailable = false;
    pthread_mutex_unlock(&notify_mutex);
}

void SignalDeviceAvailable()
{
    pthread_mutex_lock(&notify_mutex);
    newDeviceAvailable = true;
    pthread_cond_signal(&notifyNewDevice);
    pthread_mutex_unlock(&notify_mutex);
}

void *RunLoop(void * arg)
{

    runLoopSource = IONotificationPortGetRunLoopSource(gNotifyPort);

    gRunLoop = CFRunLoopGetCurrent();
    CFRunLoopAddSource(gRunLoop, runLoopSource, kCFRunLoopDefaultMode);

    // Start the run loop. Now we'll receive notifications.
    CFRunLoopRun();

    // We should never get here
    fprintf(stderr, "Unexpectedly back from CFRunLoopRun()!\n");

    return NULL;
}

void NotifyAsync(uv_work_t* req)
{
    WaitForNewDevice();
}

void NotifyFinished(uv_work_t* req)
{
    if (isRunning)
    {
        if (isAdded)
        {
            NotifyAdded(notify_item);
        }
        else
        {
            NotifyRemoved(notify_item);
        }
    }

    // Delete Item in case of removal
    if(isAdded == false)
    {
        delete notify_item;
    }

    uv_queue_work(uv_default_loop(), req, NotifyAsync, (uv_after_work_cb)NotifyFinished);
    SignalDeviceHandled();
}

void Start()
{
    isRunning = true;
}

void Stop()
{
    isRunning = false;
    pthread_mutex_lock(&notify_mutex);
    pthread_cond_signal(&notifyNewDevice);
    pthread_mutex_unlock(&notify_mutex);
}

void InitDetection()
{

    kern_return_t           kr;

    // Set up the matching criteria for the devices we're interested in. The matching criteria needs to follow
    // the same rules as kernel drivers: mainly it needs to follow the USB Common Class Specification, pp. 6-7.
    // See also Technical Q&A QA1076 "Tips on USB driver matching on Mac OS X"
    // <http://developer.apple.com/qa/qa2001/qa1076.html>.
    // One exception is that you can use the matching dictionary "as is", i.e. without adding any matching
    // criteria to it and it will match every IOUSBDevice in the system. IOServiceAddMatchingNotification will
    // consume this dictionary reference, so there is no need to release it later on.

    matchingDict = IOServiceMatching(kIOUSBDeviceClassName);    // Interested in instances of class
                                                                // IOUSBDevice and its subclasses

    if (matchingDict == NULL)
    {
        fprintf(stderr, "IOServiceMatching returned NULL.\n");
    }

    // Create a notification port and add its run loop event source to our run loop
    // This is how async notifications get set up.

    gNotifyPort = IONotificationPortCreate(kIOMasterPortDefault);

    // Now set up a notification to be called when a device is first matched by I/O Kit.
    kr = IOServiceAddMatchingNotification(gNotifyPort,                  // notifyPort
                                          kIOFirstMatchNotification,    // notificationType
                                          matchingDict,                 // matching
                                          DeviceAdded,                  // callback
                                          NULL,                         // refCon
                                          &gAddedIter                   // notification
                                          );

    if (KERN_SUCCESS != kr)
    {
        printf("IOServiceAddMatchingNotification returned 0x%08x.\n", kr);
    }

    // Iterate once to get already-present devices and arm the notification
    DeviceAdded(NULL, gAddedIter);
    intialDeviceImport = false;

    pthread_mutex_init(&notify_mutex, NULL);
    pthread_cond_init(&notifyNewDevice, NULL);
    pthread_cond_init(&notifyDeviceHandled, NULL);

    int rc = pthread_create(&lookupThread, NULL, RunLoop, NULL);
    if (rc)
    {
         printf("ERROR; return code from pthread_create() is %d\n", rc);
         exit(-1);
    }

    uv_work_t* req = new uv_work_t();
    uv_queue_work(uv_default_loop(), req, NotifyAsync, (uv_after_work_cb)NotifyFinished);

    Start();
}

void EIO_Find(uv_work_t* req)
{
    ListBaton* data = static_cast<ListBaton*>(req->data);

    CreateFilteredList(&data->results, data->vid, data->pid);
}
<commit_msg>fix stop<commit_after>#include "detection.h"
#include "deviceList.h"

#include <CoreFoundation/CoreFoundation.h>

#include <IOKit/IOKitLib.h>
#include <IOKit/IOMessage.h>
#include <IOKit/IOCFPlugIn.h>
#include <IOKit/usb/IOUSBLib.h>

#include <sys/param.h>
#include <pthread.h>

#include <uv.h>

typedef struct DeviceListItem
{
    io_object_t             notification;
    IOUSBDeviceInterface**  deviceInterface;
    DeviceItem_t*           deviceItem;
} stDeviceListItem;

static IONotificationPortRef    gNotifyPort;
static io_iterator_t            gAddedIter;
static CFRunLoopRef             gRunLoop;

CFMutableDictionaryRef  matchingDict;
CFRunLoopSourceRef      runLoopSource;

static pthread_t lookupThread;

pthread_mutex_t notify_mutex;
pthread_cond_t notifyNewDevice;
pthread_cond_t notifyDeviceHandled;

bool newDeviceAvailable = false;
bool deviceHandled = true;

ListResultItem_t* notify_item;
bool isAdded = false;
bool isRunning = false;
bool intialDeviceImport = true;

void WaitForDeviceHandled();
void SignalDeviceHandled();
void WaitForNewDevice();
void SignalDeviceAvailable();

//================================================================================================
//
//  DeviceRemoved
//
//  This routine will get called whenever any kIOGeneralInterest notification happens.  We are
//  interested in the kIOMessageServiceIsTerminated message so that's what we look for.  Other
//  messages are defined in IOMessage.h.
//
//================================================================================================
void DeviceRemoved(void *refCon, io_service_t service, natural_t messageType, void *messageArgument)
{
    kern_return_t   kr;
    stDeviceListItem* deviceListItem = (stDeviceListItem *) refCon;
    DeviceItem_t* deviceItem = deviceListItem->deviceItem;

    if (messageType == kIOMessageServiceIsTerminated)
    {
        if (deviceListItem->deviceInterface)
        {
            kr = (*deviceListItem->deviceInterface)->Release(deviceListItem->deviceInterface);
        }

        kr = IOObjectRelease(deviceListItem->notification);

        ListResultItem_t* item = NULL;
        if(deviceItem)
        {
            item = CopyElement(&deviceItem->deviceParams);
            RemoveItemFromList(deviceItem);
            delete deviceItem;
        }
        else
        {
            item = new ListResultItem_t();
        }

        WaitForDeviceHandled();
        notify_item = item;
        isAdded = false;
        SignalDeviceAvailable();

    }
}

//================================================================================================
//
//  DeviceAdded
//
//  This routine is the callback for our IOServiceAddMatchingNotification.  When we get called
//  we will look at all the devices that were added and we will:
//
//  1.  Create some private data to relate to each device (in this case we use the service's name
//      and the location ID of the device
//  2.  Submit an IOServiceAddInterestNotification of type kIOGeneralInterest for this device,
//      using the refCon field to store a pointer to our private data.  When we get called with
//      this interest notification, we can grab the refCon and access our private data.
//
//================================================================================================
void DeviceAdded(void *refCon, io_iterator_t iterator)
{
    kern_return_t       kr;
    io_service_t        usbDevice;
    IOCFPlugInInterface **plugInInterface = NULL;
    SInt32              score;
    HRESULT             res;

    while ((usbDevice = IOIteratorNext(iterator)))
    {
        io_name_t       deviceName;
        CFStringRef     deviceNameAsCFString;
        UInt32          locationID;
        UInt16          vendorId;
        UInt16          productId;
        UInt16          addr;

        DeviceItem_t* deviceItem = new DeviceItem_t();

        // Get the USB device's name.
        kr = IORegistryEntryGetName(usbDevice, deviceName);
        if (KERN_SUCCESS != kr)
        {
            deviceName[0] = '\0';
        }

        deviceNameAsCFString = CFStringCreateWithCString(kCFAllocatorDefault, deviceName, kCFStringEncodingASCII);

        if (deviceNameAsCFString)
        {
            Boolean result;
            char    deviceName[MAXPATHLEN];

            // Convert from a CFString to a C (NUL-terminated)
            result = CFStringGetCString(deviceNameAsCFString,
                                        deviceName,
                                        sizeof(deviceName),
                                        kCFStringEncodingUTF8);

            if (result)
            {
                deviceItem->deviceParams.deviceName = deviceName;
            }

            CFRelease(deviceNameAsCFString);
        }

        CFStringRef manufacturerAsCFString = (CFStringRef) IORegistryEntrySearchCFProperty(usbDevice,
                                                                                kIOServicePlane,
                                                                                CFSTR(kUSBVendorString),
                                                                                kCFAllocatorDefault,
                                                                                kIORegistryIterateRecursively);

        if (manufacturerAsCFString)
        {
            Boolean result;
            char    manufacturer[MAXPATHLEN];

            // Convert from a CFString to a C (NUL-terminated)
            result = CFStringGetCString(manufacturerAsCFString,
                                        manufacturer,
                                        sizeof(manufacturer),
                                        kCFStringEncodingUTF8);

            if (result)
            {
                deviceItem->deviceParams.manufacturer = manufacturer;
            }

            CFRelease(manufacturerAsCFString);
        }

        CFStringRef serialNumberAsCFString = (CFStringRef) IORegistryEntrySearchCFProperty(usbDevice,
                                                                                kIOServicePlane,
                                                                                CFSTR(kUSBSerialNumberString),
                                                                                kCFAllocatorDefault,
                                                                                kIORegistryIterateRecursively);

        if (serialNumberAsCFString)
        {
            Boolean result;
            char    serialNumber[MAXPATHLEN];

            // Convert from a CFString to a C (NUL-terminated)
            result = CFStringGetCString(serialNumberAsCFString,
                                        serialNumber,
                                        sizeof(serialNumber),
                                        kCFStringEncodingUTF8);

            if (result)
            {
                deviceItem->deviceParams.serialNumber = serialNumber;
            }

            CFRelease(serialNumberAsCFString);
        }

        // Now, get the locationID of this device. In order to do this, we need to create an IOUSBDeviceInterface
        // for our device. This will create the necessary connections between our userland application and the
        // kernel object for the USB Device.
        kr = IOCreatePlugInInterfaceForService(usbDevice, kIOUSBDeviceUserClientTypeID, kIOCFPlugInInterfaceID, &plugInInterface, &score);

        if ((kIOReturnSuccess != kr) || !plugInInterface)
        {
            fprintf(stderr, "IOCreatePlugInInterfaceForService returned 0x%08x.\n", kr);
            continue;
        }

        stDeviceListItem *deviceListItem = new stDeviceListItem();

        // Use the plugin interface to retrieve the device interface.
        res = (*plugInInterface)->QueryInterface(plugInInterface, CFUUIDGetUUIDBytes(kIOUSBDeviceInterfaceID), (LPVOID*) &deviceListItem->deviceInterface);

        // Now done with the plugin interface.
        (*plugInInterface)->Release(plugInInterface);

        if (res || deviceListItem->deviceInterface == NULL)
        {
            fprintf(stderr, "QueryInterface returned %d.\n", (int) res);
            continue;
        }

        // Now that we have the IOUSBDeviceInterface, we can call the routines in IOUSBLib.h.
        // In this case, fetch the locationID. The locationID uniquely identifies the device
        // and will remain the same, even across reboots, so long as the bus topology doesn't change.

        kr = (*deviceListItem->deviceInterface)->GetLocationID(deviceListItem->deviceInterface, &locationID);
        if (KERN_SUCCESS != kr)
        {
            fprintf(stderr, "GetLocationID returned 0x%08x.\n", kr);
            continue;
        }
        deviceItem->deviceParams.locationId = locationID;

        kr = (*deviceListItem->deviceInterface)->GetDeviceAddress(deviceListItem->deviceInterface, &addr);
        if (KERN_SUCCESS != kr)
        {
            fprintf(stderr, "GetDeviceAddress returned 0x%08x.\n", kr);
            continue;
        }
        deviceItem->deviceParams.deviceAddress = addr;

        kr = (*deviceListItem->deviceInterface)->GetDeviceVendor(deviceListItem->deviceInterface, &vendorId);
        if (KERN_SUCCESS != kr)
        {
            fprintf(stderr, "GetDeviceVendor returned 0x%08x.\n", kr);
            continue;
        }
        deviceItem->deviceParams.vendorId = vendorId;

        kr = (*deviceListItem->deviceInterface)->GetDeviceProduct(deviceListItem->deviceInterface, &productId);
        if (KERN_SUCCESS != kr)
        {
            fprintf(stderr, "GetDeviceProduct returned 0x%08x.\n", kr);
            continue;
        }
        deviceItem->deviceParams.productId = productId;

        // Extract path name as unique key
        io_string_t pathName;
        IORegistryEntryGetPath(usbDevice, kIOServicePlane, pathName);
        deviceNameAsCFString = CFStringCreateWithCString(kCFAllocatorDefault, pathName, kCFStringEncodingASCII);
        char cPathName[MAXPATHLEN];

        if (deviceNameAsCFString)
        {
            Boolean result;

            // Convert from a CFString to a C (NUL-terminated)
            result = CFStringGetCString(deviceNameAsCFString,
                                        cPathName,
                                        sizeof(cPathName),
                                        kCFStringEncodingUTF8);

            CFRelease(deviceNameAsCFString);
        }

        AddItemToList(cPathName, deviceItem);
        deviceListItem->deviceItem = deviceItem;

        if(intialDeviceImport == false)
        {
            WaitForDeviceHandled();
            notify_item = &deviceItem->deviceParams;
            isAdded = true;
            SignalDeviceAvailable();
        }

        // Register for an interest notification of this device being removed. Use a reference to our
        // private data as the refCon which will be passed to the notification callback.
        kr = IOServiceAddInterestNotification(gNotifyPort,                      // notifyPort
                                              usbDevice,                        // service
                                              kIOGeneralInterest,               // interestType
                                              DeviceRemoved,                    // callback
                                              deviceListItem,                   // refCon
                                              &(deviceListItem->notification)   // notification
                                              );

        if (KERN_SUCCESS != kr)
        {
            printf("IOServiceAddInterestNotification returned 0x%08x.\n", kr);
        }

        // Done with this USB device; release the reference added by IOIteratorNext
        kr = IOObjectRelease(usbDevice);
    }
}

void WaitForDeviceHandled()
{
    pthread_mutex_lock(&notify_mutex);
    if(deviceHandled == false)
    {
        pthread_cond_wait(&notifyDeviceHandled, &notify_mutex);
    }
    deviceHandled = false;
    pthread_mutex_unlock(&notify_mutex);
}

void SignalDeviceHandled()
{
    pthread_mutex_lock(&notify_mutex);
    deviceHandled = true;
    pthread_cond_signal(&notifyDeviceHandled);
    pthread_mutex_unlock(&notify_mutex);
}

void WaitForNewDevice()
{
    pthread_mutex_lock(&notify_mutex);
    if(newDeviceAvailable == false)
    {
        pthread_cond_wait(&notifyNewDevice, &notify_mutex);
    }
    newDeviceAvailable = false;
    pthread_mutex_unlock(&notify_mutex);
}

void SignalDeviceAvailable()
{
    pthread_mutex_lock(&notify_mutex);
    newDeviceAvailable = true;
    pthread_cond_signal(&notifyNewDevice);
    pthread_mutex_unlock(&notify_mutex);
}

void *RunLoop(void * arg)
{

    runLoopSource = IONotificationPortGetRunLoopSource(gNotifyPort);

    gRunLoop = CFRunLoopGetCurrent();
    CFRunLoopAddSource(gRunLoop, runLoopSource, kCFRunLoopDefaultMode);

    // Start the run loop. Now we'll receive notifications.
    CFRunLoopRun();

    // We should never get here
    fprintf(stderr, "Unexpectedly back from CFRunLoopRun()!\n");

    return NULL;
}

void NotifyAsync(uv_work_t* req)
{
    WaitForNewDevice();
}

void NotifyFinished(uv_work_t* req)
{
    if (isRunning)
    {
        if (isAdded)
        {
            NotifyAdded(notify_item);
        }
        else
        {
            NotifyRemoved(notify_item);
        }
    }

    // Delete Item in case of removal
    if(isAdded == false)
    {
        delete notify_item;
    }

    if (isRunning)
    {
        uv_queue_work(uv_default_loop(), req, NotifyAsync, (uv_after_work_cb)NotifyFinished);
    }
    SignalDeviceHandled();
}

void Start()
{
    isRunning = true;
}

void Stop()
{
    isRunning = false;
    pthread_mutex_lock(&notify_mutex);
    pthread_cond_signal(&notifyNewDevice);
    pthread_mutex_unlock(&notify_mutex);
}

void InitDetection()
{

    kern_return_t           kr;

    // Set up the matching criteria for the devices we're interested in. The matching criteria needs to follow
    // the same rules as kernel drivers: mainly it needs to follow the USB Common Class Specification, pp. 6-7.
    // See also Technical Q&A QA1076 "Tips on USB driver matching on Mac OS X"
    // <http://developer.apple.com/qa/qa2001/qa1076.html>.
    // One exception is that you can use the matching dictionary "as is", i.e. without adding any matching
    // criteria to it and it will match every IOUSBDevice in the system. IOServiceAddMatchingNotification will
    // consume this dictionary reference, so there is no need to release it later on.

    matchingDict = IOServiceMatching(kIOUSBDeviceClassName);    // Interested in instances of class
                                                                // IOUSBDevice and its subclasses

    if (matchingDict == NULL)
    {
        fprintf(stderr, "IOServiceMatching returned NULL.\n");
    }

    // Create a notification port and add its run loop event source to our run loop
    // This is how async notifications get set up.

    gNotifyPort = IONotificationPortCreate(kIOMasterPortDefault);

    // Now set up a notification to be called when a device is first matched by I/O Kit.
    kr = IOServiceAddMatchingNotification(gNotifyPort,                  // notifyPort
                                          kIOFirstMatchNotification,    // notificationType
                                          matchingDict,                 // matching
                                          DeviceAdded,                  // callback
                                          NULL,                         // refCon
                                          &gAddedIter                   // notification
                                          );

    if (KERN_SUCCESS != kr)
    {
        printf("IOServiceAddMatchingNotification returned 0x%08x.\n", kr);
    }

    // Iterate once to get already-present devices and arm the notification
    DeviceAdded(NULL, gAddedIter);
    intialDeviceImport = false;

    pthread_mutex_init(&notify_mutex, NULL);
    pthread_cond_init(&notifyNewDevice, NULL);
    pthread_cond_init(&notifyDeviceHandled, NULL);

    int rc = pthread_create(&lookupThread, NULL, RunLoop, NULL);
    if (rc)
    {
         printf("ERROR; return code from pthread_create() is %d\n", rc);
         exit(-1);
    }

    uv_work_t* req = new uv_work_t();
    uv_queue_work(uv_default_loop(), req, NotifyAsync, (uv_after_work_cb)NotifyFinished);

    Start();
}

void EIO_Find(uv_work_t* req)
{
    ListBaton* data = static_cast<ListBaton*>(req->data);

    CreateFilteredList(&data->results, data->vid, data->pid);
}
<|endoftext|>


<commit_before>/*
 * Copyright (c) 2000-2005 The Regents of The University of Michigan
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions are
 * met: redistributions of source code must retain the above copyright
 * notice, this list of conditions and the following disclaimer;
 * redistributions in binary form must reproduce the above copyright
 * notice, this list of conditions and the following disclaimer in the
 * documentation and/or other materials provided with the distribution;
 * neither the name of the copyright holders nor the names of its
 * contributors may be used to endorse or promote products derived from
 * this software without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 *
 * Authors: Steve Reinhardt
 *          Nathan Binkert
 */

/* @file
 * EventQueue interfaces
 */

#ifndef __SIM_EVENTQ_HH__
#define __SIM_EVENTQ_HH__

#include <algorithm>
#include <cassert>
#include <climits>
#include <iosfwd>
#include <string>

#include "base/flags.hh"
#include "base/misc.hh"
#include "base/types.hh"
#include "debug/Event.hh"
#include "sim/serialize.hh"

class EventQueue;       // forward declaration

extern EventQueue mainEventQueue;

/*
 * An item on an event queue.  The action caused by a given
 * event is specified by deriving a subclass and overriding the
 * process() member function.
 *
 * Caution, the order of members is chosen to maximize data packing.
 */
class Event : public Serializable
{
    friend class EventQueue;

  protected:
    typedef unsigned short FlagsType;
    typedef ::Flags<FlagsType> Flags;

    static const FlagsType PublicRead    = 0x003f; // public readable flags
    static const FlagsType PublicWrite   = 0x001d; // public writable flags
    static const FlagsType Squashed      = 0x0001; // has been squashed
    static const FlagsType Scheduled     = 0x0002; // has been scheduled
    static const FlagsType AutoDelete    = 0x0004; // delete after dispatch
    static const FlagsType AutoSerialize = 0x0008; // must be serialized
    static const FlagsType IsExitEvent   = 0x0010; // special exit event
    static const FlagsType IsMainQueue   = 0x0020; // on main event queue
    static const FlagsType Initialized   = 0x7a40; // somewhat random bits
    static const FlagsType InitMask      = 0xffc0; // mask for init bits

    bool
    initialized() const
    {
        return this && (flags & InitMask) == Initialized;
    }

  public:
    typedef int8_t Priority;

  private:
    // The event queue is now a linked list of linked lists.  The
    // 'nextBin' pointer is to find the bin, where a bin is defined as
    // when+priority.  All events in the same bin will be stored in a
    // second linked list (a stack) maintained by the 'nextInBin'
    // pointer.  The list will be accessed in LIFO order.  The end
    // result is that the insert/removal in 'nextBin' is
    // linear/constant, and the lookup/removal in 'nextInBin' is
    // constant/constant.  Hopefully this is a significant improvement
    // over the current fully linear insertion.
    Event *nextBin;
    Event *nextInBin;

    static Event *insertBefore(Event *event, Event *curr);
    static Event *removeItem(Event *event, Event *last);

    Tick _when;         //!< timestamp when event should be processed
    Priority _priority; //!< event priority
    Flags flags;

#ifndef NDEBUG
    /// Global counter to generate unique IDs for Event instances
    static Counter instanceCounter;

    /// This event's unique ID.  We can also use pointer values for
    /// this but they're not consistent across runs making debugging
    /// more difficult.  Thus we use a global counter value when
    /// debugging.
    Counter instance;

    /// queue to which this event belongs (though it may or may not be
    /// scheduled on this queue yet)
    EventQueue *queue;
#endif

#ifdef EVENTQ_DEBUG
    Tick whenCreated;   //!< time created
    Tick whenScheduled; //!< time scheduled
#endif

    void
    setWhen(Tick when, EventQueue *q)
    {
        _when = when;
#ifndef NDEBUG
        queue = q;
#endif
#ifdef EVENTQ_DEBUG
        whenScheduled = curTick();
#endif
    }

  protected:
    /// Accessor for flags.
    Flags
    getFlags() const
    {
        return flags & PublicRead;
    }

    bool
    isFlagSet(Flags _flags) const
    {
        assert(_flags.noneSet(~PublicRead));
        return flags.isSet(_flags);
    }

    /// Accessor for flags.
    void
    setFlags(Flags _flags)
    {
        assert(_flags.noneSet(~PublicWrite));
        flags.set(_flags);
    }

    void
    clearFlags(Flags _flags)
    {
        assert(_flags.noneSet(~PublicWrite));
        flags.clear(_flags);
    }

    void
    clearFlags()
    {
        flags.clear(PublicWrite);
    }

    // This function isn't really useful if TRACING_ON is not defined
    virtual void trace(const char *action);     //!< trace event activity

  public:
    /// Event priorities, to provide tie-breakers for events scheduled
    /// at the same cycle.  Most events are scheduled at the default
    /// priority; these values are used to control events that need to
    /// be ordered within a cycle.

    /// Minimum priority
    static const Priority Minimum_Pri =          SCHAR_MIN;

    /// If we enable tracing on a particular cycle, do that as the
    /// very first thing so we don't miss any of the events on
    /// that cycle (even if we enter the debugger).
    static const Priority Trace_Enable_Pri =          -101;

    /// Breakpoints should happen before anything else (except
    /// enabling trace output), so we don't miss any action when
    /// debugging.
    static const Priority Debug_Break_Pri =           -100;

    /// CPU switches schedule the new CPU's tick event for the
    /// same cycle (after unscheduling the old CPU's tick event).
    /// The switch needs to come before any tick events to make
    /// sure we don't tick both CPUs in the same cycle.
    static const Priority CPU_Switch_Pri =             -31;

    /// For some reason "delayed" inter-cluster writebacks are
    /// scheduled before regular writebacks (which have default
    /// priority).  Steve?
    static const Priority Delayed_Writeback_Pri =       -1;

    /// Default is zero for historical reasons.
    static const Priority Default_Pri =                  0;

    /// Serailization needs to occur before tick events also, so
    /// that a serialize/unserialize is identical to an on-line
    /// CPU switch.
    static const Priority Serialize_Pri =               32;

    /// CPU ticks must come after other associated CPU events
    /// (such as writebacks).
    static const Priority CPU_Tick_Pri =                50;

    /// Statistics events (dump, reset, etc.) come after
    /// everything else, but before exit.
    static const Priority Stat_Event_Pri =              90;

    /// Progress events come at the end.
    static const Priority Progress_Event_Pri =          95;

    /// If we want to exit on this cycle, it's the very last thing
    /// we do.
    static const Priority Sim_Exit_Pri =               100;

    /// Maximum priority
    static const Priority Maximum_Pri =          SCHAR_MAX;

    /*
     * Event constructor
     * @param queue that the event gets scheduled on
     */
    Event(Priority p = Default_Pri, Flags f = 0)
        : nextBin(NULL), nextInBin(NULL), _priority(p),
          flags(Initialized | f)
    {
        assert(f.noneSet(~PublicWrite));
#ifndef NDEBUG
        instance = ++instanceCounter;
        queue = NULL;
#endif
#ifdef EVENTQ_DEBUG
        whenCreated = curTick();
        whenScheduled = 0;
#endif
    }

    virtual ~Event();
    virtual const std::string name() const;

    /// Return a C string describing the event.  This string should
    /// *not* be dynamically allocated; just a const char array
    /// describing the event class.
    virtual const char *description() const;

    /// Dump the current event data
    void dump() const;

  public:
    /*
     * This member function is invoked when the event is processed
     * (occurs).  There is no default implementation; each subclass
     * must provide its own implementation.  The event is not
     * automatically deleted after it is processed (to allow for
     * statically allocated event objects).
     *
     * If the AutoDestroy flag is set, the object is deleted once it
     * is processed.
     */
    virtual void process() = 0;

    /// Determine if the current event is scheduled
    bool scheduled() const { return flags.isSet(Scheduled); }

    /// Squash the current event
    void squash() { flags.set(Squashed); }

    /// Check whether the event is squashed
    bool squashed() const { return flags.isSet(Squashed); }

    /// See if this is a SimExitEvent (without resorting to RTTI)
    bool isExitEvent() const { return flags.isSet(IsExitEvent); }

    /// Get the time that the event is scheduled
    Tick when() const { return _when; }

    /// Get the event priority
    Priority priority() const { return _priority; }

#ifndef SWIG
    virtual void serialize(std::ostream &os);
    virtual void unserialize(Checkpoint *cp, const std::string &section);
#endif
};

#ifndef SWIG
inline bool
operator<(const Event &l, const Event &r)
{
    return l.when() < r.when() ||
        (l.when() == r.when() && l.priority() < r.priority());
}

inline bool
operator>(const Event &l, const Event &r)
{
    return l.when() > r.when() ||
        (l.when() == r.when() && l.priority() > r.priority());
}

inline bool
operator<=(const Event &l, const Event &r)
{
    return l.when() < r.when() ||
        (l.when() == r.when() && l.priority() <= r.priority());
}
inline bool
operator>=(const Event &l, const Event &r)
{
    return l.when() > r.when() ||
        (l.when() == r.when() && l.priority() >= r.priority());
}

inline bool
operator==(const Event &l, const Event &r)
{
    return l.when() == r.when() && l.priority() == r.priority();
}

inline bool
operator!=(const Event &l, const Event &r)
{
    return l.when() != r.when() || l.priority() != r.priority();
}
#endif

/*
 * Queue of events sorted in time order
 */
class EventQueue : public Serializable
{
  private:
    std::string objName;
    Event *head;
    Tick _curTick;

    void insert(Event *event);
    void remove(Event *event);

    EventQueue(const EventQueue &);
    const EventQueue &operator=(const EventQueue &);

  public:
    EventQueue(const std::string &n);

    virtual const std::string name() const { return objName; }

    // schedule the given event on this queue
    void schedule(Event *event, Tick when);
    void deschedule(Event *event);
    void reschedule(Event *event, Tick when, bool always = false);

    Tick nextTick() const { return head->when(); }
    void setCurTick(Tick newVal) { _curTick = newVal; }
    Tick getCurTick() { return _curTick; }

    Event *serviceOne();

    // process all events up to the given timestamp.  we inline a
    // quick test to see if there are any events to process; if so,
    // call the internal out-of-line version to process them all.
    void
    serviceEvents(Tick when)
    {
        while (!empty()) {
            if (nextTick() > when)
                break;

            /**
             * @todo this assert is a good bug catcher.  I need to
             * make it true again.
             */
            //assert(head->when() >= when && "event scheduled in the past");
            serviceOne();
        }

        setCurTick(when);
    }

    // return true if no events are queued
    bool empty() const { return head == NULL; }

    void dump() const;

    bool debugVerify() const;

    /**
     *  function for replacing the head of the event queue, so that a
     *  different set of events can run without disturbing events that have
     *  already been scheduled. Already scheduled events can be processed
     *  by replacing the original head back.
     *  USING THIS FUNCTION CAN BE DANGEROUS TO THE HEALTH OF THE SIMULATOR.
     *  NOT RECOMMENDED FOR USE.
     */
    Event* replaceHead(Event* s);

#ifndef SWIG
    virtual void serialize(std::ostream &os);
    virtual void unserialize(Checkpoint *cp, const std::string &section);
#endif
};

void dumpMainQueue();

#ifndef SWIG
class EventManager
{
  protected:
    /** A pointer to this object's event queue */
    EventQueue *eventq;

  public:
    EventManager(EventManager &em) : eventq(em.eventq) {}
    EventManager(EventManager *em) : eventq(em->eventq) {}
    EventManager(EventQueue *eq) : eventq(eq) {}

    EventQueue *
    eventQueue() const
    {
        return eventq;
    }

    void
    schedule(Event &event, Tick when)
    {
        eventq->schedule(&event, when);
    }

    void
    deschedule(Event &event)
    {
        eventq->deschedule(&event);
    }

    void
    reschedule(Event &event, Tick when, bool always = false)
    {
        eventq->reschedule(&event, when, always);
    }

    void
    schedule(Event *event, Tick when)
    {
        eventq->schedule(event, when);
    }

    void
    deschedule(Event *event)
    {
        eventq->deschedule(event);
    }

    void
    reschedule(Event *event, Tick when, bool always = false)
    {
        eventq->reschedule(event, when, always);
    }

    void setCurTick(Tick newVal) { eventq->setCurTick(newVal); }
};

template <class T, void (T::* F)()>
void
DelayFunction(EventQueue *eventq, Tick when, T *object)
{
    class DelayEvent : public Event
    {
      private:
        T *object;

      public:
        DelayEvent(T *o)
            : Event(Default_Pri, AutoDelete), object(o)
        { }
        void process() { (object->*F)(); }
        const char *description() const { return "delay"; }
    };

    eventq->schedule(new DelayEvent(object), when);
}

template <class T, void (T::* F)()>
class EventWrapper : public Event
{
  private:
    T *object;

  public:
    EventWrapper(T *obj, bool del = false, Priority p = Default_Pri)
        : Event(p), object(obj)
    {
        if (del)
            setFlags(AutoDelete);
    }

    EventWrapper(T &obj, bool del = false, Priority p = Default_Pri)
        : Event(p), object(&obj)
    {
        if (del)
            setFlags(AutoDelete);
    }

    void process() { (object->*F)(); }

    const std::string
    name() const
    {
        return object->name() + ".wrapped_event";
    }

    const char *description() const { return "EventWrapped"; }
};
#endif

#endif // __SIM_EVENTQ_HH__
<commit_msg>sim: fix event priority name for debug-start option<commit_after>/*
 * Copyright (c) 2000-2005 The Regents of The University of Michigan
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions are
 * met: redistributions of source code must retain the above copyright
 * notice, this list of conditions and the following disclaimer;
 * redistributions in binary form must reproduce the above copyright
 * notice, this list of conditions and the following disclaimer in the
 * documentation and/or other materials provided with the distribution;
 * neither the name of the copyright holders nor the names of its
 * contributors may be used to endorse or promote products derived from
 * this software without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 *
 * Authors: Steve Reinhardt
 *          Nathan Binkert
 */

/* @file
 * EventQueue interfaces
 */

#ifndef __SIM_EVENTQ_HH__
#define __SIM_EVENTQ_HH__

#include <algorithm>
#include <cassert>
#include <climits>
#include <iosfwd>
#include <string>

#include "base/flags.hh"
#include "base/misc.hh"
#include "base/types.hh"
#include "debug/Event.hh"
#include "sim/serialize.hh"

class EventQueue;       // forward declaration

extern EventQueue mainEventQueue;

/*
 * An item on an event queue.  The action caused by a given
 * event is specified by deriving a subclass and overriding the
 * process() member function.
 *
 * Caution, the order of members is chosen to maximize data packing.
 */
class Event : public Serializable
{
    friend class EventQueue;

  protected:
    typedef unsigned short FlagsType;
    typedef ::Flags<FlagsType> Flags;

    static const FlagsType PublicRead    = 0x003f; // public readable flags
    static const FlagsType PublicWrite   = 0x001d; // public writable flags
    static const FlagsType Squashed      = 0x0001; // has been squashed
    static const FlagsType Scheduled     = 0x0002; // has been scheduled
    static const FlagsType AutoDelete    = 0x0004; // delete after dispatch
    static const FlagsType AutoSerialize = 0x0008; // must be serialized
    static const FlagsType IsExitEvent   = 0x0010; // special exit event
    static const FlagsType IsMainQueue   = 0x0020; // on main event queue
    static const FlagsType Initialized   = 0x7a40; // somewhat random bits
    static const FlagsType InitMask      = 0xffc0; // mask for init bits

    bool
    initialized() const
    {
        return this && (flags & InitMask) == Initialized;
    }

  public:
    typedef int8_t Priority;

  private:
    // The event queue is now a linked list of linked lists.  The
    // 'nextBin' pointer is to find the bin, where a bin is defined as
    // when+priority.  All events in the same bin will be stored in a
    // second linked list (a stack) maintained by the 'nextInBin'
    // pointer.  The list will be accessed in LIFO order.  The end
    // result is that the insert/removal in 'nextBin' is
    // linear/constant, and the lookup/removal in 'nextInBin' is
    // constant/constant.  Hopefully this is a significant improvement
    // over the current fully linear insertion.
    Event *nextBin;
    Event *nextInBin;

    static Event *insertBefore(Event *event, Event *curr);
    static Event *removeItem(Event *event, Event *last);

    Tick _when;         //!< timestamp when event should be processed
    Priority _priority; //!< event priority
    Flags flags;

#ifndef NDEBUG
    /// Global counter to generate unique IDs for Event instances
    static Counter instanceCounter;

    /// This event's unique ID.  We can also use pointer values for
    /// this but they're not consistent across runs making debugging
    /// more difficult.  Thus we use a global counter value when
    /// debugging.
    Counter instance;

    /// queue to which this event belongs (though it may or may not be
    /// scheduled on this queue yet)
    EventQueue *queue;
#endif

#ifdef EVENTQ_DEBUG
    Tick whenCreated;   //!< time created
    Tick whenScheduled; //!< time scheduled
#endif

    void
    setWhen(Tick when, EventQueue *q)
    {
        _when = when;
#ifndef NDEBUG
        queue = q;
#endif
#ifdef EVENTQ_DEBUG
        whenScheduled = curTick();
#endif
    }

  protected:
    /// Accessor for flags.
    Flags
    getFlags() const
    {
        return flags & PublicRead;
    }

    bool
    isFlagSet(Flags _flags) const
    {
        assert(_flags.noneSet(~PublicRead));
        return flags.isSet(_flags);
    }

    /// Accessor for flags.
    void
    setFlags(Flags _flags)
    {
        assert(_flags.noneSet(~PublicWrite));
        flags.set(_flags);
    }

    void
    clearFlags(Flags _flags)
    {
        assert(_flags.noneSet(~PublicWrite));
        flags.clear(_flags);
    }

    void
    clearFlags()
    {
        flags.clear(PublicWrite);
    }

    // This function isn't really useful if TRACING_ON is not defined
    virtual void trace(const char *action);     //!< trace event activity

  public:
    /// Event priorities, to provide tie-breakers for events scheduled
    /// at the same cycle.  Most events are scheduled at the default
    /// priority; these values are used to control events that need to
    /// be ordered within a cycle.

    /// Minimum priority
    static const Priority Minimum_Pri =          SCHAR_MIN;

    /// If we enable tracing on a particular cycle, do that as the
    /// very first thing so we don't miss any of the events on
    /// that cycle (even if we enter the debugger).
    static const Priority Debug_Enable_Pri =          -101;

    /// Breakpoints should happen before anything else (except
    /// enabling trace output), so we don't miss any action when
    /// debugging.
    static const Priority Debug_Break_Pri =           -100;

    /// CPU switches schedule the new CPU's tick event for the
    /// same cycle (after unscheduling the old CPU's tick event).
    /// The switch needs to come before any tick events to make
    /// sure we don't tick both CPUs in the same cycle.
    static const Priority CPU_Switch_Pri =             -31;

    /// For some reason "delayed" inter-cluster writebacks are
    /// scheduled before regular writebacks (which have default
    /// priority).  Steve?
    static const Priority Delayed_Writeback_Pri =       -1;

    /// Default is zero for historical reasons.
    static const Priority Default_Pri =                  0;

    /// Serailization needs to occur before tick events also, so
    /// that a serialize/unserialize is identical to an on-line
    /// CPU switch.
    static const Priority Serialize_Pri =               32;

    /// CPU ticks must come after other associated CPU events
    /// (such as writebacks).
    static const Priority CPU_Tick_Pri =                50;

    /// Statistics events (dump, reset, etc.) come after
    /// everything else, but before exit.
    static const Priority Stat_Event_Pri =              90;

    /// Progress events come at the end.
    static const Priority Progress_Event_Pri =          95;

    /// If we want to exit on this cycle, it's the very last thing
    /// we do.
    static const Priority Sim_Exit_Pri =               100;

    /// Maximum priority
    static const Priority Maximum_Pri =          SCHAR_MAX;

    /*
     * Event constructor
     * @param queue that the event gets scheduled on
     */
    Event(Priority p = Default_Pri, Flags f = 0)
        : nextBin(NULL), nextInBin(NULL), _priority(p),
          flags(Initialized | f)
    {
        assert(f.noneSet(~PublicWrite));
#ifndef NDEBUG
        instance = ++instanceCounter;
        queue = NULL;
#endif
#ifdef EVENTQ_DEBUG
        whenCreated = curTick();
        whenScheduled = 0;
#endif
    }

    virtual ~Event();
    virtual const std::string name() const;

    /// Return a C string describing the event.  This string should
    /// *not* be dynamically allocated; just a const char array
    /// describing the event class.
    virtual const char *description() const;

    /// Dump the current event data
    void dump() const;

  public:
    /*
     * This member function is invoked when the event is processed
     * (occurs).  There is no default implementation; each subclass
     * must provide its own implementation.  The event is not
     * automatically deleted after it is processed (to allow for
     * statically allocated event objects).
     *
     * If the AutoDestroy flag is set, the object is deleted once it
     * is processed.
     */
    virtual void process() = 0;

    /// Determine if the current event is scheduled
    bool scheduled() const { return flags.isSet(Scheduled); }

    /// Squash the current event
    void squash() { flags.set(Squashed); }

    /// Check whether the event is squashed
    bool squashed() const { return flags.isSet(Squashed); }

    /// See if this is a SimExitEvent (without resorting to RTTI)
    bool isExitEvent() const { return flags.isSet(IsExitEvent); }

    /// Get the time that the event is scheduled
    Tick when() const { return _when; }

    /// Get the event priority
    Priority priority() const { return _priority; }

#ifndef SWIG
    virtual void serialize(std::ostream &os);
    virtual void unserialize(Checkpoint *cp, const std::string &section);
#endif
};

#ifndef SWIG
inline bool
operator<(const Event &l, const Event &r)
{
    return l.when() < r.when() ||
        (l.when() == r.when() && l.priority() < r.priority());
}

inline bool
operator>(const Event &l, const Event &r)
{
    return l.when() > r.when() ||
        (l.when() == r.when() && l.priority() > r.priority());
}

inline bool
operator<=(const Event &l, const Event &r)
{
    return l.when() < r.when() ||
        (l.when() == r.when() && l.priority() <= r.priority());
}
inline bool
operator>=(const Event &l, const Event &r)
{
    return l.when() > r.when() ||
        (l.when() == r.when() && l.priority() >= r.priority());
}

inline bool
operator==(const Event &l, const Event &r)
{
    return l.when() == r.when() && l.priority() == r.priority();
}

inline bool
operator!=(const Event &l, const Event &r)
{
    return l.when() != r.when() || l.priority() != r.priority();
}
#endif

/*
 * Queue of events sorted in time order
 */
class EventQueue : public Serializable
{
  private:
    std::string objName;
    Event *head;
    Tick _curTick;

    void insert(Event *event);
    void remove(Event *event);

    EventQueue(const EventQueue &);
    const EventQueue &operator=(const EventQueue &);

  public:
    EventQueue(const std::string &n);

    virtual const std::string name() const { return objName; }

    // schedule the given event on this queue
    void schedule(Event *event, Tick when);
    void deschedule(Event *event);
    void reschedule(Event *event, Tick when, bool always = false);

    Tick nextTick() const { return head->when(); }
    void setCurTick(Tick newVal) { _curTick = newVal; }
    Tick getCurTick() { return _curTick; }

    Event *serviceOne();

    // process all events up to the given timestamp.  we inline a
    // quick test to see if there are any events to process; if so,
    // call the internal out-of-line version to process them all.
    void
    serviceEvents(Tick when)
    {
        while (!empty()) {
            if (nextTick() > when)
                break;

            /**
             * @todo this assert is a good bug catcher.  I need to
             * make it true again.
             */
            //assert(head->when() >= when && "event scheduled in the past");
            serviceOne();
        }

        setCurTick(when);
    }

    // return true if no events are queued
    bool empty() const { return head == NULL; }

    void dump() const;

    bool debugVerify() const;

    /**
     *  function for replacing the head of the event queue, so that a
     *  different set of events can run without disturbing events that have
     *  already been scheduled. Already scheduled events can be processed
     *  by replacing the original head back.
     *  USING THIS FUNCTION CAN BE DANGEROUS TO THE HEALTH OF THE SIMULATOR.
     *  NOT RECOMMENDED FOR USE.
     */
    Event* replaceHead(Event* s);

#ifndef SWIG
    virtual void serialize(std::ostream &os);
    virtual void unserialize(Checkpoint *cp, const std::string &section);
#endif
};

void dumpMainQueue();

#ifndef SWIG
class EventManager
{
  protected:
    /** A pointer to this object's event queue */
    EventQueue *eventq;

  public:
    EventManager(EventManager &em) : eventq(em.eventq) {}
    EventManager(EventManager *em) : eventq(em->eventq) {}
    EventManager(EventQueue *eq) : eventq(eq) {}

    EventQueue *
    eventQueue() const
    {
        return eventq;
    }

    void
    schedule(Event &event, Tick when)
    {
        eventq->schedule(&event, when);
    }

    void
    deschedule(Event &event)
    {
        eventq->deschedule(&event);
    }

    void
    reschedule(Event &event, Tick when, bool always = false)
    {
        eventq->reschedule(&event, when, always);
    }

    void
    schedule(Event *event, Tick when)
    {
        eventq->schedule(event, when);
    }

    void
    deschedule(Event *event)
    {
        eventq->deschedule(event);
    }

    void
    reschedule(Event *event, Tick when, bool always = false)
    {
        eventq->reschedule(event, when, always);
    }

    void setCurTick(Tick newVal) { eventq->setCurTick(newVal); }
};

template <class T, void (T::* F)()>
void
DelayFunction(EventQueue *eventq, Tick when, T *object)
{
    class DelayEvent : public Event
    {
      private:
        T *object;

      public:
        DelayEvent(T *o)
            : Event(Default_Pri, AutoDelete), object(o)
        { }
        void process() { (object->*F)(); }
        const char *description() const { return "delay"; }
    };

    eventq->schedule(new DelayEvent(object), when);
}

template <class T, void (T::* F)()>
class EventWrapper : public Event
{
  private:
    T *object;

  public:
    EventWrapper(T *obj, bool del = false, Priority p = Default_Pri)
        : Event(p), object(obj)
    {
        if (del)
            setFlags(AutoDelete);
    }

    EventWrapper(T &obj, bool del = false, Priority p = Default_Pri)
        : Event(p), object(&obj)
    {
        if (del)
            setFlags(AutoDelete);
    }

    void process() { (object->*F)(); }

    const std::string
    name() const
    {
        return object->name() + ".wrapped_event";
    }

    const char *description() const { return "EventWrapped"; }
};
#endif

#endif // __SIM_EVENTQ_HH__
<|endoftext|>


<commit_before>/*=========================================================================

Program:   Visualization Toolkit
Module:    vtkSQLDatabase.cxx

Copyright (c) Ken Martin, Will Schroeder, Bill Lorensen
All rights reserved.
See Copyright.txt or http://www.kitware.com/Copyright.htm for details.

This software is distributed WITHOUT ANY WARRANTY; without even
the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
PURPOSE.  See the above copyright notice for more information.

=========================================================================*/
/*-------------------------------------------------------------------------
  Copyright 2008 Sandia Corporation.
  Under the terms of Contract DE-AC04-94AL85000 with Sandia Corporation,
  the U.S. Government retains certain rights in this software.
-------------------------------------------------------------------------*/

#include "vtkTextCodec.h"

const char* vtkTextCodec::Name()
{
  return "";
}

bool vtkTextCodec::CanHandle(const char*)
{
  return false;
}

bool vtkTextCodec::IsValid(istream&)
{
  return false;
}

vtkTextCodec::~vtkTextCodec()
{
}

vtkTextCodec::vtkTextCodec()
{
}

namespace
{
  class vtkUnicodeStringOutputIterator : public vtkTextCodec::OutputIterator
  {
  public:
    virtual vtkUnicodeStringOutputIterator& operator++(int);
    virtual vtkUnicodeStringOutputIterator& operator*();
    virtual vtkUnicodeStringOutputIterator& operator=(const vtkUnicodeString::value_type value);

    vtkUnicodeStringOutputIterator(vtkUnicodeString& outputString);
    ~vtkUnicodeStringOutputIterator();

  private:
    vtkUnicodeStringOutputIterator(); // Not implemented
    vtkUnicodeStringOutputIterator(const vtkUnicodeStringOutputIterator&); // Not implemented
    const vtkUnicodeStringOutputIterator& operator=(const vtkUnicodeStringOutputIterator&); // Not Implemented

    vtkUnicodeString& OutputString;
    unsigned int StringPosition;
  };

  vtkUnicodeStringOutputIterator& vtkUnicodeStringOutputIterator::operator++(int)
  {
    this->StringPosition++;
    return *this;
  }

  vtkUnicodeStringOutputIterator& vtkUnicodeStringOutputIterator::operator*()
  {
    return *this;
  }

  vtkUnicodeStringOutputIterator& vtkUnicodeStringOutputIterator::operator=(const vtkUnicodeString::value_type value)
  {
    this->OutputString += value;
    return *this;
  }

  vtkUnicodeStringOutputIterator::vtkUnicodeStringOutputIterator(vtkUnicodeString& outputString) :
    OutputString(outputString), StringPosition(0)
  {
  }

  vtkUnicodeStringOutputIterator::~vtkUnicodeStringOutputIterator()
  {
  }
}

vtkUnicodeString vtkTextCodec::ToUnicode(istream& InputStream)
{
  // create an output string stream
  vtkUnicodeString returnString;

  vtkUnicodeStringOutputIterator StringIterator(returnString);
  this->ToUnicode(InputStream, StringIterator);

  return returnString;
}

void vtkTextCodec::PrintSelf(ostream& os, vtkIndent indent)
{
  os << indent << "vtkTextCodec (" << this << ") \n";
  indent = indent.GetNextIndent();
  this->Superclass::PrintSelf(os, indent.GetNextIndent());
}
<commit_msg>Removed unused method<commit_after>/*=========================================================================

Program:   Visualization Toolkit
Module:    vtkSQLDatabase.cxx

Copyright (c) Ken Martin, Will Schroeder, Bill Lorensen
All rights reserved.
See Copyright.txt or http://www.kitware.com/Copyright.htm for details.

This software is distributed WITHOUT ANY WARRANTY; without even
the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
PURPOSE.  See the above copyright notice for more information.

=========================================================================*/
/*-------------------------------------------------------------------------
  Copyright 2008 Sandia Corporation.
  Under the terms of Contract DE-AC04-94AL85000 with Sandia Corporation,
  the U.S. Government retains certain rights in this software.
-------------------------------------------------------------------------*/

#include "vtkTextCodec.h"

const char* vtkTextCodec::Name()
{
  return "";
}

bool vtkTextCodec::CanHandle(const char*)
{
  return false;
}

bool vtkTextCodec::IsValid(istream&)
{
  return false;
}

vtkTextCodec::~vtkTextCodec()
{
}

vtkTextCodec::vtkTextCodec()
{
}

namespace
{
  class vtkUnicodeStringOutputIterator : public vtkTextCodec::OutputIterator
  {
  public:
    virtual vtkUnicodeStringOutputIterator& operator++(int);
    virtual vtkUnicodeStringOutputIterator& operator*();
    virtual vtkUnicodeStringOutputIterator& operator=(const vtkUnicodeString::value_type value);

    vtkUnicodeStringOutputIterator(vtkUnicodeString& outputString);
    ~vtkUnicodeStringOutputIterator();

  private:
    vtkUnicodeStringOutputIterator(const vtkUnicodeStringOutputIterator&); // Not implemented
    const vtkUnicodeStringOutputIterator& operator=(const vtkUnicodeStringOutputIterator&); // Not Implemented

    vtkUnicodeString& OutputString;
    unsigned int StringPosition;
  };

  vtkUnicodeStringOutputIterator& vtkUnicodeStringOutputIterator::operator++(int)
  {
    this->StringPosition++;
    return *this;
  }

  vtkUnicodeStringOutputIterator& vtkUnicodeStringOutputIterator::operator*()
  {
    return *this;
  }

  vtkUnicodeStringOutputIterator& vtkUnicodeStringOutputIterator::operator=(const vtkUnicodeString::value_type value)
  {
    this->OutputString += value;
    return *this;
  }

  vtkUnicodeStringOutputIterator::vtkUnicodeStringOutputIterator(vtkUnicodeString& outputString) :
    OutputString(outputString), StringPosition(0)
  {
  }

  vtkUnicodeStringOutputIterator::~vtkUnicodeStringOutputIterator()
  {
  }
}

vtkUnicodeString vtkTextCodec::ToUnicode(istream& InputStream)
{
  // create an output string stream
  vtkUnicodeString returnString;

  vtkUnicodeStringOutputIterator StringIterator(returnString);
  this->ToUnicode(InputStream, StringIterator);

  return returnString;
}

void vtkTextCodec::PrintSelf(ostream& os, vtkIndent indent)
{
  os << indent << "vtkTextCodec (" << this << ") \n";
  indent = indent.GetNextIndent();
  this->Superclass::PrintSelf(os, indent.GetNextIndent());
}
<|endoftext|>


<commit_before>#include <sstream>
#include <iostream>

#include <QDir>
#include <QDebug>
#include <QFileInfo>
#include <QEventLoop>
#include <QMutexLocker>
#include <QNetworkReply>
#include <QNetworkRequest>
#include <QCoreApplication>

#include "Util.h"
#include "Downloader.h"
#include "DownloadTask.h"

namespace efdl {
  Downloader::Downloader(const QUrl &url)
    : url{url}, conns{1}, chunks{-1}, chunkSize{-1}, downloadCount{0},
    rangeCount{0}, contentLen{0}, offset{0}, confirm{false}, resume{false},
    verbose{false}, dryRun{false}, showHeaders{false}, resumable{false},
    reply{nullptr}
  {
    connect(&commitThread, &CommitThread::finished,
            this, &Downloader::onCommitThreadFinished);
    connect(this, &Downloader::chunkToThread,
            &commitThread, &CommitThread::enqueueChunk,
            Qt::QueuedConnection);
  }

  void Downloader::setHttpCredentials(const QString &user,
                                      const QString &pass) {
    httpUser = user;
    httpPass = pass;
  }

  void Downloader::start() {
    // Fetch HEAD to find out the size but also if it exists.
    reply = getHead(url);
    if (!reply) {
      QCoreApplication::exit(-1);
      return;
    }
    url = reply->url();

    // Find "Content-Length".
    if (!reply->hasRawHeader("Content-Length")) {
      qCritical() << "ERROR Invalid response from server. No"
                  << "'Content-Length' header present!";
      QCoreApplication::exit(-1);
      return;
    }
    bool ok;
    contentLen =
      QString::fromUtf8(reply->rawHeader("Content-Length")).toLongLong(&ok);
    if (!ok || contentLen == 0) {
      qCritical() << "ERROR Invalid content length:" << contentLen;
      QCoreApplication::exit(-1);
      return;
    }

    QString type;
    if (reply->hasRawHeader("Content-Type")) {
      type = reply->rawHeader("Content-Type");
      int pos;
      if ((pos = type.indexOf(";")) != -1) {
        type = type.mid(0, pos);
      }
    }

    qDebug() << "File size" << qPrintable(Util::formatSize(contentLen, 1))
             << qPrintable(!type.isEmpty() ? "[" + type + "]" : "");

    // Check for header "Accept-Ranges" and whether it has "bytes"
    // supported.
    resumable = false;
    if (reply->hasRawHeader("Accept-Ranges")) {
      const QString ranges = QString::fromUtf8(reply->rawHeader("Accept-Ranges"));
      resumable = ranges.toLower().contains("bytes");
    }
    if (verbose) {
      qDebug() << qPrintable(QString("%1RESUMABLE").
                             arg(resumable ? "" : "NOT "));
    }

    if (!resumable && resume) {
      qCritical() << "ERROR Cannot resume because server doesn't support it!";
      QCoreApplication::exit(-1);
      return;
    }

    // Clean reply.
    reply->close();
    reply = nullptr;

    // If performing a dry run then stop now.
    if (dryRun) {
      emit finished();
      return;
    }

    if (!setupFile()) {
      QCoreApplication::exit(-1);
      return;
    }

    createRanges();
    setupThreadPool();

    emit information(outputPath, contentLen, rangeCount, conns, offset);

    // Start actual download.
    download();
  }

  void Downloader::onDownloadTaskFinished(int num, Range range, QByteArray *data) {
    QMutexLocker locker{&finishedMutex};
    chunksMap[range.first] = data;
    downloadCount++;
    saveChunk();

    emit chunkFinished(num, range);
  }

  void Downloader::onDownloadTaskFailed(int num, Range range, int httpCode,
                                        QNetworkReply::NetworkError error) {
    emit chunkFailed(num, range, httpCode, error);
  }

  void Downloader::onCommitThreadFinished() {
    emit finished();
  }

  QNetworkReply *Downloader::getHead(const QUrl &url) {
    if (verbose) {
      qDebug() << "HEAD" << qPrintable(url.toString(QUrl::FullyEncoded));
    }

    QNetworkRequest req{url};
    req.setRawHeader("Accept-Encoding", "identity");

    if (!httpUser.isEmpty() && !httpPass.isEmpty()) {
      req.setRawHeader("Authorization",
                       Util::createHttpAuthHeader(httpUser, httpPass));
    }

    auto *rep = netmgr.head(req);
    QEventLoop loop;
    connect(rep, &QNetworkReply::finished, &loop, &QEventLoop::quit);
    loop.exec();

    if (rep->error() != QNetworkReply::NoError) {
      rep->abort();
      qCritical() << "ERROR" << qPrintable(Util::getErrorString(rep->error()));
      return nullptr;
    }

    int code = rep->attribute(QNetworkRequest::HttpStatusCodeAttribute).toInt();
    if (verbose) {
      qDebug() << "CODE" << code;
      if (showHeaders) {
        qDebug() << "HEADERS"
                 << qPrintable(Util::formatHeaders(rep->rawHeaderPairs()));
      }
    }

    static bool didRedir = false;

    if (code >= 200 && code < 300) {
      if (url != this->url) {
        qDebug() << "Resolved to"
                 << qPrintable(url.toString(QUrl::FullyEncoded));
      }

      if (confirm && didRedir) {
        if (!Util::askProceed(tr("Do you want to continue?") + " [y/N] ")) {
          rep->abort();
          qCritical() << "Aborting..";
          return nullptr;
        }
      }
    }

    // Handle redirect.
    else if (code >= 300 && code < 400) {
      if (!rep->hasRawHeader("Location")) {
        rep->abort();
        qCritical() << "ERROR Could not resolve URL!";
        return nullptr;
      }

      QString locHdr = QString::fromUtf8(rep->rawHeader("Location"));
      QUrl loc{locHdr};
      if (!loc.isValid()) {
        rep->abort();
        qCritical() << "ERROR Invalid redirection header:"
                    << qPrintable(loc.toString(QUrl::FullyEncoded));
        return nullptr;
      }

      // If relative then try resolving with the previous URL.
      if (loc.isRelative()) {
        loc = url.resolved(loc);
      }

      if (verbose) {
        qDebug() << "REDIRECT" << qPrintable(loc.toString(QUrl::FullyEncoded));
      }

      didRedir = true;
      rep->abort();
      rep = getHead(loc);
    }

    // Client errors.
    else if (code >= 400 && code < 500) {
      rep->abort();
      qDebug() << "CLIENT ERROR";
      return nullptr;
    }

    // Server errors.
    else if (code >= 500 && code < 600) {
      rep->abort();
      qDebug() << "SERVER ERROR";
      return nullptr;
    }

    return rep;
  }

  bool Downloader::setupFile() {
    QFileInfo fi{url.path()};
    QDir dir = (outputDir.isEmpty() ? QDir::current() : outputDir);
    outputPath = dir.absoluteFilePath(fi.fileName());
    qDebug() << "Saving to" << qPrintable(outputPath);

    auto *file = new QFile{outputPath};
    if (file->exists() && !resume) {
      if (!QFile::remove(outputPath)) {
        qCritical() << "ERROR Could not truncate output file!";
        delete file;
        return false;
      }
    }

    if (resume) {
      qint64 fileSize{file->size()};
      if (fileSize >= contentLen) {
        qCritical() << "Cannot resume download because the size is larger than or"
                    << "equal:" << qPrintable(Util::formatSize(fileSize, 1))
                    << "vs." << qPrintable(Util::formatSize(contentLen, 1));
        if (confirm &&
            !Util::askProceed("Do you want to truncate file and continue? [y/N] ")) {
          qCritical() << "Aborting..";
          delete file;
          return false;
        }
        if (!confirm) {
          qDebug() << "Truncating file";
        }
        resume = false;
      }
      else if (fileSize > 0) {
        offset = fileSize;
        float perc = (long double)offset / (long double)contentLen * 100.0;
        qDebug() << "Resuming at offset"
                 << qPrintable(Util::formatSize(offset, 1))
                 << qPrintable(QString("(%1%)").arg(perc, 0, 'f', 1));
      }
    }

    QIODevice::OpenMode openMode{QIODevice::WriteOnly};
    if (resume) {
      openMode |= QIODevice::Append;
    }
    else {
      openMode |= QIODevice::Truncate;
    }

    if (!file->open(openMode)) {
      qCritical() << "ERROR Could not open file for writing!";
      delete file;
      return false;
    }
    commitThread.setFile(file);

    return true;
  }

  void Downloader::createRanges() {
    ranges.clear();
    chunksMap.clear();

    qint64 size = 1048576;
    if (chunkSize != -1) {
      size = chunkSize;
    }
    else if (chunks != -1) {
      size = (contentLen - offset) / chunks;
    }
    else if (conns >= 8) {
      size = (contentLen - offset) / conns;
      constexpr qint64 MB{10485760}; // 10 MB
      if (size > MB) size = MB;
    }

    if (verbose) {
      qDebug() << "CHUNK SIZE" << qPrintable(Util::formatSize(size, 1));
    }

    for (qint64 start = offset; start < contentLen; start += size) {
      qint64 end = start + size;
      if (end >= contentLen) {
        end = contentLen;
      }
      ranges.enqueue(Range{start, end - 1});
      chunksMap[start] = nullptr;
    }
    rangeCount = ranges.size();

    if (verbose) {
      qDebug() << "CHUNKS" << rangeCount;
    }
  }

  void Downloader::setupThreadPool() {
    // Cap connections to the amount of chunks to download.
    if (conns > ranges.size()) {
      int old{conns};
      conns = ranges.size();
      qDebug() << "Connections capped to chunks:" << old << "->" << conns;
    }

    pool.setMaxThreadCount(conns);
  }

  void Downloader::download() {
    // Fill queue with tasks and start immediately.
    int num{1};
    while (!ranges.empty()) {
      auto range = ranges.dequeue();
      auto *task = new DownloadTask{url, range, num++, httpUser, httpPass};
      connect(task, SIGNAL(started(int)), SIGNAL(chunkStarted(int)));
      connect(task, SIGNAL(progress(int, qint64, qint64)),
              SIGNAL(chunkProgress(int, qint64, qint64)));
      connect(task, &DownloadTask::finished,
              this, &Downloader::onDownloadTaskFinished);
      connect(task, &DownloadTask::failed,
              this, &Downloader::onDownloadTaskFailed);
      pool.start(task);
    }
  }

  void Downloader::saveChunk() {
    // Lock on chunks map is required to be acquired going into this
    // method!

    if (chunksMap.isEmpty()) {
      // Wait for commit thread to be done.
      return;
    }

    auto key = chunksMap.firstKey();
    const auto *value = chunksMap[key];
    if (value != nullptr) {
      emit chunkToThread(value, chunksMap.size() == 1);
      if (!commitThread.isRunning()) {
        commitThread.start();
      }
      chunksMap.remove(key);
    }

    // If everything has been downloaded then call method again.
    if (rangeCount == downloadCount) {
      saveChunk();
    }
  }
}
<commit_msg>Emulates a HTTP HEAD with a HTTP GET using Range 0-0 so that no body is downloaded. This is necessary because some HTTP servers yield different headers for HEAD and GET.<commit_after>#include <sstream>
#include <iostream>

#include <QDir>
#include <QDebug>
#include <QFileInfo>
#include <QEventLoop>
#include <QMutexLocker>
#include <QNetworkReply>
#include <QNetworkRequest>
#include <QCoreApplication>

#include "Util.h"
#include "Downloader.h"
#include "DownloadTask.h"

namespace efdl {
  Downloader::Downloader(const QUrl &url)
    : url{url}, conns{1}, chunks{-1}, chunkSize{-1}, downloadCount{0},
    rangeCount{0}, contentLen{0}, offset{0}, confirm{false}, resume{false},
    verbose{false}, dryRun{false}, showHeaders{false}, resumable{false},
    reply{nullptr}
  {
    connect(&commitThread, &CommitThread::finished,
            this, &Downloader::onCommitThreadFinished);
    connect(this, &Downloader::chunkToThread,
            &commitThread, &CommitThread::enqueueChunk,
            Qt::QueuedConnection);
  }

  void Downloader::setHttpCredentials(const QString &user,
                                      const QString &pass) {
    httpUser = user;
    httpPass = pass;
  }

  void Downloader::start() {
    // Fetch HEAD to find out the size but also if it exists.
    reply = getHead(url);
    if (!reply) {
      QCoreApplication::exit(-1);
      return;
    }
    url = reply->url();

    // Find "Content-Length".
    if (!reply->hasRawHeader("Content-Length")) {
      qCritical() << "ERROR Invalid response from server. No"
                  << "'Content-Length' header present!";
      QCoreApplication::exit(-1);
      return;
    }
    bool ok;
    contentLen =
      QString::fromUtf8(reply->rawHeader("Content-Length")).toLongLong(&ok);
    if (!ok || contentLen == 0) {
      qCritical() << "ERROR Invalid content length:" << contentLen;
      QCoreApplication::exit(-1);
      return;
    }

    // Check if the total is different than the Content-Length and, if
    // so, then change to that number.
    if (reply->hasRawHeader("Content-Range")) {
      QString range = QString::fromUtf8(reply->rawHeader("Content-Range"));
      QStringList elms = range.split("/", QString::SkipEmptyParts);
      if (elms.size() == 2) {
        qint64 tot = elms[1].toLongLong(&ok);
        if (ok && tot > 0 && tot != contentLen) {
          contentLen = tot;
        }
      }
    }

    QString type;
    if (reply->hasRawHeader("Content-Type")) {
      type = reply->rawHeader("Content-Type");
      int pos;
      if ((pos = type.indexOf(";")) != -1) {
        type = type.mid(0, pos);
      }
    }

    qDebug() << "File size" << qPrintable(Util::formatSize(contentLen, 1))
             << qPrintable(!type.isEmpty() ? "[" + type + "]" : "");

    // Check for header "Accept-Ranges" and whether it has "bytes"
    // supported.
    resumable = false;
    if (reply->hasRawHeader("Accept-Ranges")) {
      const QString ranges = QString::fromUtf8(reply->rawHeader("Accept-Ranges"));
      resumable = ranges.toLower().contains("bytes");
    }
    if (verbose) {
      qDebug() << qPrintable(QString("%1RESUMABLE").
                             arg(resumable ? "" : "NOT "));
    }

    if (!resumable && resume) {
      qCritical() << "ERROR Cannot resume because server doesn't support it!";
      QCoreApplication::exit(-1);
      return;
    }

    // Clean reply.
    reply->close();
    reply = nullptr;

    // If performing a dry run then stop now.
    if (dryRun) {
      emit finished();
      return;
    }

    if (!setupFile()) {
      QCoreApplication::exit(-1);
      return;
    }

    createRanges();
    setupThreadPool();

    emit information(outputPath, contentLen, rangeCount, conns, offset);

    // Start actual download.
    download();
  }

  void Downloader::onDownloadTaskFinished(int num, Range range, QByteArray *data) {
    QMutexLocker locker{&finishedMutex};
    chunksMap[range.first] = data;
    downloadCount++;
    saveChunk();

    emit chunkFinished(num, range);
  }

  void Downloader::onDownloadTaskFailed(int num, Range range, int httpCode,
                                        QNetworkReply::NetworkError error) {
    emit chunkFailed(num, range, httpCode, error);
  }

  void Downloader::onCommitThreadFinished() {
    emit finished();
  }

  QNetworkReply *Downloader::getHead(const QUrl &url) {
    if (verbose) {
      qDebug() << "HEAD" << qPrintable(url.toString(QUrl::FullyEncoded));
    }

    // Emulating a HEAD by doing a GET which only retrieves range
    // 0-0. This is necessary because some sites return different
    // headers for HEAD/GET even though they should be the same!

    QNetworkRequest req{url};
    req.setRawHeader("Range", QString("bytes=0-0").toUtf8());
    req.setRawHeader("Accept-Encoding", "identity");

    if (!httpUser.isEmpty() && !httpPass.isEmpty()) {
      req.setRawHeader("Authorization",
                       Util::createHttpAuthHeader(httpUser, httpPass));
    }

    //auto *rep = netmgr.head(req);
    auto *rep = netmgr.get(req);

    QEventLoop loop;
    connect(rep, &QNetworkReply::finished, &loop, &QEventLoop::quit);
    loop.exec();

    if (rep->error() != QNetworkReply::NoError) {
      rep->abort();
      qCritical() << "ERROR" << qPrintable(Util::getErrorString(rep->error()));
      return nullptr;
    }

    int code = rep->attribute(QNetworkRequest::HttpStatusCodeAttribute).toInt();
    if (verbose) {
      qDebug() << "CODE" << code;
      if (showHeaders) {
        qDebug() << "HEADERS"
                 << qPrintable(Util::formatHeaders(rep->rawHeaderPairs()));
      }
    }

    static bool didRedir = false;

    if (code >= 200 && code < 300) {
      if (url != this->url) {
        qDebug() << "Resolved to"
                 << qPrintable(url.toString(QUrl::FullyEncoded));
      }

      if (confirm && didRedir) {
        if (!Util::askProceed(tr("Do you want to continue?") + " [y/N] ")) {
          rep->abort();
          qCritical() << "Aborting..";
          return nullptr;
        }
      }
    }

    // Handle redirect.
    else if (code >= 300 && code < 400) {
      if (!rep->hasRawHeader("Location")) {
        rep->abort();
        qCritical() << "ERROR Could not resolve URL!";
        return nullptr;
      }

      QString locHdr = QString::fromUtf8(rep->rawHeader("Location"));
      QUrl loc{locHdr};
      if (!loc.isValid()) {
        rep->abort();
        qCritical() << "ERROR Invalid redirection header:"
                    << qPrintable(loc.toString(QUrl::FullyEncoded));
        return nullptr;
      }

      // If relative then try resolving with the previous URL.
      if (loc.isRelative()) {
        loc = url.resolved(loc);
      }

      if (verbose) {
        qDebug() << "REDIRECT" << qPrintable(loc.toString(QUrl::FullyEncoded));
      }

      didRedir = true;
      rep->abort();
      rep = getHead(loc);
    }

    // Client errors.
    else if (code >= 400 && code < 500) {
      rep->abort();
      qDebug() << "CLIENT ERROR";
      return nullptr;
    }

    // Server errors.
    else if (code >= 500 && code < 600) {
      rep->abort();
      qDebug() << "SERVER ERROR";
      return nullptr;
    }

    return rep;
  }

  bool Downloader::setupFile() {
    QFileInfo fi{url.path()};
    QDir dir = (outputDir.isEmpty() ? QDir::current() : outputDir);
    outputPath = dir.absoluteFilePath(fi.fileName());
    qDebug() << "Saving to" << qPrintable(outputPath);

    auto *file = new QFile{outputPath};
    if (file->exists() && !resume) {
      if (!QFile::remove(outputPath)) {
        qCritical() << "ERROR Could not truncate output file!";
        delete file;
        return false;
      }
    }

    if (resume) {
      qint64 fileSize{file->size()};
      if (fileSize >= contentLen) {
        qCritical() << "Cannot resume download because the size is larger than or"
                    << "equal:" << qPrintable(Util::formatSize(fileSize, 1))
                    << "vs." << qPrintable(Util::formatSize(contentLen, 1));
        if (confirm &&
            !Util::askProceed("Do you want to truncate file and continue? [y/N] ")) {
          qCritical() << "Aborting..";
          delete file;
          return false;
        }
        if (!confirm) {
          qDebug() << "Truncating file";
        }
        resume = false;
      }
      else if (fileSize > 0) {
        offset = fileSize;
        float perc = (long double)offset / (long double)contentLen * 100.0;
        qDebug() << "Resuming at offset"
                 << qPrintable(Util::formatSize(offset, 1))
                 << qPrintable(QString("(%1%)").arg(perc, 0, 'f', 1));
      }
    }

    QIODevice::OpenMode openMode{QIODevice::WriteOnly};
    if (resume) {
      openMode |= QIODevice::Append;
    }
    else {
      openMode |= QIODevice::Truncate;
    }

    if (!file->open(openMode)) {
      qCritical() << "ERROR Could not open file for writing!";
      delete file;
      return false;
    }
    commitThread.setFile(file);

    return true;
  }

  void Downloader::createRanges() {
    ranges.clear();
    chunksMap.clear();

    qint64 size = 1048576;
    if (chunkSize != -1) {
      size = chunkSize;
    }
    else if (chunks != -1) {
      size = (contentLen - offset) / chunks;
    }
    else if (conns >= 8) {
      size = (contentLen - offset) / conns;
      constexpr qint64 MB{10485760}; // 10 MB
      if (size > MB) size = MB;
    }

    if (verbose) {
      qDebug() << "CHUNK SIZE" << qPrintable(Util::formatSize(size, 1));
    }

    for (qint64 start = offset; start < contentLen; start += size) {
      qint64 end = start + size;
      if (end >= contentLen) {
        end = contentLen;
      }
      ranges.enqueue(Range{start, end - 1});
      chunksMap[start] = nullptr;
    }
    rangeCount = ranges.size();

    if (verbose) {
      qDebug() << "CHUNKS" << rangeCount;
    }
  }

  void Downloader::setupThreadPool() {
    // Cap connections to the amount of chunks to download.
    if (conns > ranges.size()) {
      int old{conns};
      conns = ranges.size();
      qDebug() << "Connections capped to chunks:" << old << "->" << conns;
    }

    pool.setMaxThreadCount(conns);
  }

  void Downloader::download() {
    // Fill queue with tasks and start immediately.
    int num{1};
    while (!ranges.empty()) {
      auto range = ranges.dequeue();
      auto *task = new DownloadTask{url, range, num++, httpUser, httpPass};
      connect(task, SIGNAL(started(int)), SIGNAL(chunkStarted(int)));
      connect(task, SIGNAL(progress(int, qint64, qint64)),
              SIGNAL(chunkProgress(int, qint64, qint64)));
      connect(task, &DownloadTask::finished,
              this, &Downloader::onDownloadTaskFinished);
      connect(task, &DownloadTask::failed,
              this, &Downloader::onDownloadTaskFailed);
      pool.start(task);
    }
  }

  void Downloader::saveChunk() {
    // Lock on chunks map is required to be acquired going into this
    // method!

    if (chunksMap.isEmpty()) {
      // Wait for commit thread to be done.
      return;
    }

    auto key = chunksMap.firstKey();
    const auto *value = chunksMap[key];
    if (value != nullptr) {
      emit chunkToThread(value, chunksMap.size() == 1);
      if (!commitThread.isRunning()) {
        commitThread.start();
      }
      chunksMap.remove(key);
    }

    // If everything has been downloaded then call method again.
    if (rangeCount == downloadCount) {
      saveChunk();
    }
  }
}
<|endoftext|>


<commit_before>/*
 * Copyright (C) 2011 The Android Open Source Project
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

#include "SkAdvancedTypefaceMetrics.h"
#include "SkTypeface.h"
#include "SkFontHost.h"

#define TRACE_LIFECYCLE

#ifdef TRACE_LIFECYCLE
    static int32_t gTypefaceCounter;
#endif

SkTypeface::SkTypeface(Style style, SkFontID fontID, bool isFixedWidth)
    : fUniqueID(fontID), fStyle(style), fIsFixedWidth(isFixedWidth) {
#ifdef TRACE_LIFECYCLE
    SkDebugf("SkTypeface: create  %p fontID %d total %d\n",
             this, fontID, ++gTypefaceCounter);
#endif
}

SkTypeface::~SkTypeface() {
#ifdef TRACE_LIFECYCLE
    SkDebugf("SkTypeface: destroy %p fontID %d total %d\n",
             this, fUniqueID, --gTypefaceCounter);
#endif
}

///////////////////////////////////////////////////////////////////////////////

uint32_t SkTypeface::UniqueID(const SkTypeface* face) {
    if (face) {
        return face->uniqueID();
    }

    // We cache the default fontID, assuming it will not change during a boot
    // The initial value of 0 is fine, since a typeface's uniqueID should not
    // be zero.
    static uint32_t gDefaultFontID;

    if (0 == gDefaultFontID) {
        SkTypeface* defaultFace =
                SkFontHost::CreateTypeface(NULL, NULL, NULL, 0,
                                           SkTypeface::kNormal);
        SkASSERT(defaultFace);
        gDefaultFontID = defaultFace->uniqueID();
        defaultFace->unref();
    }
    return gDefaultFontID;
}

bool SkTypeface::Equal(const SkTypeface* facea, const SkTypeface* faceb) {
    return SkTypeface::UniqueID(facea) == SkTypeface::UniqueID(faceb);
}

///////////////////////////////////////////////////////////////////////////////

SkTypeface* SkTypeface::CreateFromName(const char name[], Style style) {
    return SkFontHost::CreateTypeface(NULL, name, NULL, 0, style);
}

SkTypeface* SkTypeface::CreateForChars(const void* data, size_t bytelength,
                                       Style s) {
    return SkFontHost::CreateTypeface(NULL, NULL, data, bytelength, s);
}

SkTypeface* SkTypeface::CreateFromTypeface(const SkTypeface* family, Style s) {
    return SkFontHost::CreateTypeface(family, NULL, NULL, 0, s);
}

SkTypeface* SkTypeface::CreateFromStream(SkStream* stream) {
    return SkFontHost::CreateTypefaceFromStream(stream);
}

SkTypeface* SkTypeface::CreateFromFile(const char path[]) {
    return SkFontHost::CreateTypefaceFromFile(path);
}

///////////////////////////////////////////////////////////////////////////////

void SkTypeface::serialize(SkWStream* stream) const {
    SkFontHost::Serialize(this, stream);
}

SkTypeface* SkTypeface::Deserialize(SkStream* stream) {
    return SkFontHost::Deserialize(stream);
}

SkAdvancedTypefaceMetrics* SkTypeface::getAdvancedTypefaceMetrics(
        SkAdvancedTypefaceMetrics::PerGlyphInfo perGlyphInfo) const {
    return SkFontHost::GetAdvancedTypefaceMetrics(fUniqueID, perGlyphInfo);
}
<commit_msg>disable lifecycle dumps<commit_after>/*
 * Copyright (C) 2011 The Android Open Source Project
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

#include "SkAdvancedTypefaceMetrics.h"
#include "SkTypeface.h"
#include "SkFontHost.h"

//#define TRACE_LIFECYCLE

#ifdef TRACE_LIFECYCLE
    static int32_t gTypefaceCounter;
#endif

SkTypeface::SkTypeface(Style style, SkFontID fontID, bool isFixedWidth)
    : fUniqueID(fontID), fStyle(style), fIsFixedWidth(isFixedWidth) {
#ifdef TRACE_LIFECYCLE
    SkDebugf("SkTypeface: create  %p fontID %d total %d\n",
             this, fontID, ++gTypefaceCounter);
#endif
}

SkTypeface::~SkTypeface() {
#ifdef TRACE_LIFECYCLE
    SkDebugf("SkTypeface: destroy %p fontID %d total %d\n",
             this, fUniqueID, --gTypefaceCounter);
#endif
}

///////////////////////////////////////////////////////////////////////////////

uint32_t SkTypeface::UniqueID(const SkTypeface* face) {
    if (face) {
        return face->uniqueID();
    }

    // We cache the default fontID, assuming it will not change during a boot
    // The initial value of 0 is fine, since a typeface's uniqueID should not
    // be zero.
    static uint32_t gDefaultFontID;

    if (0 == gDefaultFontID) {
        SkTypeface* defaultFace =
                SkFontHost::CreateTypeface(NULL, NULL, NULL, 0,
                                           SkTypeface::kNormal);
        SkASSERT(defaultFace);
        gDefaultFontID = defaultFace->uniqueID();
        defaultFace->unref();
    }
    return gDefaultFontID;
}

bool SkTypeface::Equal(const SkTypeface* facea, const SkTypeface* faceb) {
    return SkTypeface::UniqueID(facea) == SkTypeface::UniqueID(faceb);
}

///////////////////////////////////////////////////////////////////////////////

SkTypeface* SkTypeface::CreateFromName(const char name[], Style style) {
    return SkFontHost::CreateTypeface(NULL, name, NULL, 0, style);
}

SkTypeface* SkTypeface::CreateForChars(const void* data, size_t bytelength,
                                       Style s) {
    return SkFontHost::CreateTypeface(NULL, NULL, data, bytelength, s);
}

SkTypeface* SkTypeface::CreateFromTypeface(const SkTypeface* family, Style s) {
    return SkFontHost::CreateTypeface(family, NULL, NULL, 0, s);
}

SkTypeface* SkTypeface::CreateFromStream(SkStream* stream) {
    return SkFontHost::CreateTypefaceFromStream(stream);
}

SkTypeface* SkTypeface::CreateFromFile(const char path[]) {
    return SkFontHost::CreateTypefaceFromFile(path);
}

///////////////////////////////////////////////////////////////////////////////

void SkTypeface::serialize(SkWStream* stream) const {
    SkFontHost::Serialize(this, stream);
}

SkTypeface* SkTypeface::Deserialize(SkStream* stream) {
    return SkFontHost::Deserialize(stream);
}

SkAdvancedTypefaceMetrics* SkTypeface::getAdvancedTypefaceMetrics(
        SkAdvancedTypefaceMetrics::PerGlyphInfo perGlyphInfo) const {
    return SkFontHost::GetAdvancedTypefaceMetrics(fUniqueID, perGlyphInfo);
}
<|endoftext|>