globus_result_t
globus_i_xio_system_file_try_write(
    globus_xio_system_file_t            handle,
    const globus_xio_iovec_t *          iov,
    int                                 iovc,
    globus_size_t *                     nbytes)
{
    globus_result_t                     result;
    GlobusXIOName(globus_i_xio_system_file_try_write);

    GlobusXIOSystemDebugEnter();

    if(iovc == 1)
    {
        result = globus_i_xio_system_try_write(
            handle, iov->iov_base, iov->iov_len, nbytes);
    }
    else
    {
        result = globus_i_xio_system_try_writev(handle, iov, iovc, nbytes);
    }

    GlobusXIOSystemDebugExit();
    return result;
}
globus_result_t
globus_i_xio_win32_complete(
    globus_callback_func_t              callback,
    void *                              user_arg)
{
    globus_l_xio_win32_poll_entry_t *   entry;
    globus_result_t                     result;
    GlobusXIOName(globus_i_xio_win32_complete);

    GlobusXIOSystemDebugEnter();
    
    if (! globus_i_am_only_thread())
    {
        return globus_callback_register_oneshot(
            0,
            0,
            callback,
            user_arg);
    }

    win32_mutex_lock(&globus_l_xio_win32_poll_lock);
    {
        if(globus_l_xio_win32_poll_free)
        {
            entry = globus_l_xio_win32_poll_free;
            globus_l_xio_win32_poll_free = entry->next;
        }
        else
        {
            entry = (globus_l_xio_win32_poll_entry_t *)
                globus_malloc(sizeof(globus_l_xio_win32_poll_entry_t));
            if(!entry)
            {
                result = GlobusXIOErrorMemory("entry");
                goto error_malloc;
            }
        }
        
        entry->callback = callback;
        entry->user_arg = user_arg;
    
        GlobusLWin32PollQueueEnqueue(entry);
        
        if(globus_l_xio_win32_poll_event_sleeping &&
            !globus_l_xio_win32_poll_event_pending)
        {
            SetEvent(globus_l_xio_win32_poll_event);
            globus_l_xio_win32_poll_event_pending = GLOBUS_TRUE;
        }
    }
    win32_mutex_unlock(&globus_l_xio_win32_poll_lock);
    
    GlobusXIOSystemDebugExit();

    return GLOBUS_SUCCESS;

error_malloc:
    return result;
}
globus_result_t
globus_i_xio_system_socket_try_write(
    globus_xio_system_socket_t          handle,
    const globus_xio_iovec_t *          iov,
    int                                 iovc,
    int                                 flags,
    globus_sockaddr_t *                 to,
    globus_size_t *                     nbytes)
{
    globus_result_t                     result;
    GlobusXIOName(globus_i_xio_system_socket_try_write);

    GlobusXIOSystemDebugEnter();

#if !defined(WIN32) && !defined(TARGET_ARCH_NETOS)
    /* posix can use writev for sockets */
    if(!flags && !to && iovc > 1)
    {
        result = globus_i_xio_system_try_writev(handle, iov, iovc, nbytes);
    }
    else
#endif
    if(iovc == 1)
    {
        if(to)
        {
            result = globus_i_xio_system_try_sendto(
                handle, iov->iov_base, iov->iov_len, flags, to, nbytes);
        }
        else
        {
            result = globus_i_xio_system_try_send(
                handle, iov->iov_base, iov->iov_len, flags, nbytes);
        }
    }
    else
    {
        struct msghdr                   msghdr;

        memset(&msghdr, 0, sizeof(msghdr));
        msghdr.msg_iov = (struct iovec *) iov;
        msghdr.msg_iovlen = iovc;
        if(to)
        {
            msghdr.msg_name = (struct sockaddr *) to;
            msghdr.msg_namelen = GlobusLibcSockaddrLen(to);
        }

        result = globus_i_xio_system_try_sendmsg(
            handle, &msghdr, flags, nbytes);
    }

    GlobusXIOSystemDebugExit();
    return result;
}
globus_result_t
globus_i_xio_system_socket_try_read(
    globus_xio_system_socket_t          handle,
    const globus_xio_iovec_t *          iov,
    int                                 iovc,
    int                                 flags,
    globus_sockaddr_t *                 from,
    globus_size_t *                     nbytes)
{
    globus_result_t                     result;
    GlobusXIOName(globus_i_xio_system_socket_try_read);

    GlobusXIOSystemDebugEnter();

#if !defined(WIN32) && !defined(TARGET_ARCH_NETOS)
    /* posix can use readv for sockets */
    if(!flags && !from && iovc > 1)
    {
        result = globus_i_xio_system_try_readv(handle, iov, iovc, nbytes);
    }
    else
#endif
    if(iovc == 1)
    {
        if(from)
        {
            result = globus_i_xio_system_try_recvfrom(
                handle, iov->iov_base, iov->iov_len, flags, from, nbytes);
        }
        else
        {
            result = globus_i_xio_system_try_recv(
                handle, iov->iov_base, iov->iov_len, flags, nbytes);
        }
    }
    else
    {
        struct msghdr                   msghdr;

        memset(&msghdr, 0, sizeof(msghdr));
        msghdr.msg_iov = (struct iovec *) iov;
        msghdr.msg_iovlen = iovc;
        if(from)
        {
            msghdr.msg_name = from;
            msghdr.msg_namelen = sizeof(globus_sockaddr_t);
        }

        result = globus_i_xio_system_try_recvmsg(
            handle, &msghdr, flags, nbytes);
    }

    GlobusXIOSystemDebugExit();
    return result;
}
int
globus_i_xio_win32_complete_activate(void)
{
    globus_result_t                     result;
    globus_reltime_t                    period;
    GlobusXIOName(globus_i_xio_win32_complete_activate);
    
    GlobusXIOSystemDebugEnter();
    
    if (!globus_i_am_only_thread())
    {
        goto skip_activate;
    }
    GlobusLWin32PollQueueInit();
    win32_mutex_init(&globus_l_xio_win32_poll_lock, 0);
    globus_l_xio_win32_poll_event_sleeping = GLOBUS_FALSE;
    globus_l_xio_win32_poll_event_pending = GLOBUS_FALSE;
    globus_l_xio_win32_poll_free = 0;
    
    globus_l_xio_win32_poll_event = CreateEvent(0, FALSE, FALSE, 0);
    if(globus_l_xio_win32_poll_event == 0)
    {
        goto error_event;
    }
    
    GlobusTimeReltimeSet(period, 0, 0);
    result = globus_callback_register_periodic(
        &globus_l_xio_win32_poll_handle,
        0,
        &period,
        globus_l_xio_win32_poll,
        0);
    if(result != GLOBUS_SUCCESS)
    {
        goto error_periodic;
    }
    
    globus_callback_add_wakeup_handler(
        globus_l_xio_win32_wakeup_handler, 0);
    
skip_activate:
    GlobusXIOSystemDebugExit();
    
    return GLOBUS_SUCCESS;
    
error_periodic:
    CloseHandle(globus_l_xio_win32_poll_event);
    globus_l_xio_win32_poll_event = 0;
error_event:
    win32_mutex_destroy(&globus_l_xio_win32_poll_lock);
    GlobusXIOSystemDebugExitWithError();
    return GLOBUS_FAILURE;
}
int
globus_i_xio_system_common_deactivate(void)
{
    GlobusXIOName(globus_i_xio_system_common_deactivate);

    GlobusXIOSystemDebugEnter();

    globus_module_deactivate(GLOBUS_XIO_MODULE);

    GlobusXIOSystemDebugExit();
    GlobusDebugDestroy(GLOBUS_XIO_SYSTEM);
    return GLOBUS_SUCCESS;
}
static
void
globus_l_xio_win32_blocking_destroy(
    globus_l_xio_win32_blocking_info_t * blocking_info)
{
    GlobusXIOName(globus_l_xio_win32_blocking_destroy);
    
    GlobusXIOSystemDebugEnter();
    
    WSACloseEvent(blocking_info->event);
    globus_free(blocking_info);
    
    GlobusXIOSystemDebugExit();
}
static
void
globus_l_xio_win32_wakeup_handler(
    void *                              user_arg)
{
    GlobusXIOName(globus_l_xio_win32_wakeup_handler);

    GlobusXIOSystemDebugEnter();
    
    if(globus_l_xio_win32_poll_event != 0)
    {
        SetEvent(globus_l_xio_win32_poll_event);
    }
    
    GlobusXIOSystemDebugExit();
}
static
void
globus_l_xio_win32_blocking_cb(
    globus_result_t                     result,
    globus_size_t                       nbytes,
    void *                              user_arg)
{
    globus_l_xio_win32_blocking_info_t * blocking_info;
    GlobusXIOName(globus_l_xio_win32_blocking_cb);
    
    GlobusXIOSystemDebugEnter();
    
    blocking_info = (globus_l_xio_win32_blocking_info_t *) user_arg;
    if(result != GLOBUS_SUCCESS)
    {
        blocking_info->error = globus_error_get(result);
    }
    blocking_info->nbytes = nbytes;
    SetEvent(blocking_info->event);
    
    GlobusXIOSystemDebugExit();
}
static
globus_result_t
globus_l_xio_win32_blocking_init(
    globus_l_xio_win32_blocking_info_t ** u_blocking_info)
{
    globus_l_xio_win32_blocking_info_t * blocking_info;
    globus_result_t                     result;
    GlobusXIOName(globus_l_xio_win32_blocking_init);
    
    GlobusXIOSystemDebugEnter();
    
    blocking_info = (globus_l_xio_win32_blocking_info_t *)
        globus_calloc(1, sizeof(globus_l_xio_win32_blocking_info_t));
    if(!blocking_info)
    {
        result = GlobusXIOErrorMemory("blocking_info");
        goto error_info;
    }
    
    blocking_info->event = CreateEvent(0, FALSE, FALSE, 0);
    if(blocking_info->event == 0)
    {
        result = GlobusXIOErrorSystemError(
            "CreateEvent", GetLastError());
        goto error_create;
    }
    
    *u_blocking_info = blocking_info;
    
    GlobusXIOSystemDebugExit();
    return GLOBUS_SUCCESS;

error_create:
    globus_free(blocking_info);
error_info:
    *u_blocking_info = 0;
    GlobusXIOSystemDebugExitWithError();
    return result;
}
static
void
globus_l_xio_win32_unregister_poll_cb(
    void *                              user_arg)
{
    globus_l_shutdown_info_t *          info;
    GlobusXIOName(globus_l_xio_win32_unregister_poll_cb);
    
    GlobusXIOSystemDebugEnter();
    
    info = (globus_l_shutdown_info_t *) user_arg;
    
    globus_mutex_lock(&info->mutex);
    {
        CloseHandle(globus_l_xio_win32_poll_event);
        globus_l_xio_win32_poll_event = 0;
        globus_cond_signal(&info->cond);
    }
    globus_mutex_unlock(&info->mutex);
    
    GlobusXIOSystemDebugExit();
}
static
void
globus_l_xio_win32_socket_cancel_cb(
    globus_xio_operation_t              op,
    void *                              user_arg,
    globus_xio_error_type_t             reason)
{
    globus_i_xio_system_op_info_t *     op_info;
    GlobusXIOName(globus_l_xio_win32_socket_cancel_cb);

    GlobusXIOSystemDebugEnter();

    op_info = (globus_i_xio_system_op_info_t *) user_arg;
    
    /* this access of the handle is not safe if users destroy it
     * with outstanding callbacks.  I don't think that is allowed, so we
     * should be ok.
     */
    win32_mutex_lock(&op_info->handle->lock);
    {
        if(op_info->state != GLOBUS_I_XIO_SYSTEM_OP_COMPLETE && 
            op_info->state != GLOBUS_I_XIO_SYSTEM_OP_CANCELED)
        {
            op_info->error = reason == GLOBUS_XIO_ERROR_TIMEOUT
                ? GlobusXIOErrorObjTimeout()
                : GlobusXIOErrorObjCanceled();
            
            if(op_info->state == GLOBUS_I_XIO_SYSTEM_OP_NEW)
            {
                op_info->state = GLOBUS_I_XIO_SYSTEM_OP_CANCELED;
                    
                GlobusXIOSystemDebugPrintf(
                    GLOBUS_I_XIO_SYSTEM_DEBUG_INFO,
                    ("[%s] fd=%lu, Canceling NEW\n",
                        _xio_name, (unsigned long)op_info->handle->socket));
            }
            else
            {
                globus_result_t         result;

                op_info->state = GLOBUS_I_XIO_SYSTEM_OP_COMPLETE;
                
                GlobusXIOSystemDebugPrintf(
                    GLOBUS_I_XIO_SYSTEM_DEBUG_INFO,
                    ("[%s] fd=%lu, Canceling Pending\n",
                        _xio_name, (unsigned long)op_info->handle->socket));
                
                if(op_info->handle->read_info == op_info)
                {
                    op_info->handle->read_info = 0;
                }
                else
                {
                    globus_assert(op_info->handle->write_info == op_info);
                    op_info->handle->write_info = 0;
                }
                
                /* unregister and kickout now */
                result = globus_callback_register_oneshot(
                    0,
                    0,
                    globus_l_xio_win32_socket_kickout,
                    op_info);
                /* really cant do anything else */
                if(result != GLOBUS_SUCCESS)
                {
                    globus_panic(
                        GLOBUS_XIO_SYSTEM_MODULE,
                        result,
                        _XIOSL("[%s:%d] Couldn't register callback"),
                        _xio_name,
                        __LINE__);
                }
            }
        }
    }
    win32_mutex_unlock(&op_info->handle->lock);

    GlobusXIOSystemDebugExit();
}
int
globus_i_xio_system_common_activate(void)
{
    GlobusXIOName(globus_i_xio_system_common_activate);

    GlobusDebugInit(GLOBUS_XIO_SYSTEM, TRACE DATA INFO RAW);

    GlobusXIOSystemDebugEnter();

    /* I am going to leave this memory around after deactivation.  To safely
     * destroy them, I would need a lot more synchronization of kicked out
     * callbacks
     */
    if(globus_module_activate(GLOBUS_XIO_MODULE) != GLOBUS_SUCCESS)
    {
        goto error_activate;
    }

    if(!globus_l_xio_system_memory_initialized)
    {
        globus_l_xio_system_memory_initialized = GLOBUS_TRUE;
        globus_memory_init(
            &globus_i_xio_system_op_info_memory,
            sizeof(globus_i_xio_system_op_info_t),
            10);
        globus_memory_init(
            &globus_i_xio_system_iov_memory, sizeof(struct iovec) * 10, 10);
    }

    globus_l_xio_iov_max = -1;

#ifdef _SC_IOV_MAX
    if (globus_l_xio_iov_max == -1)
    {
        /* If this returns -1, the limit might be indefinite if errno is
         * unchanged, but that doesn't mean infinite. It's unclear what
         * one is to do in that case
         */
        globus_l_xio_iov_max = sysconf(_SC_IOV_MAX);
    }
#endif

#ifdef IOV_MAX
    if (globus_l_xio_iov_max == -1)
    {
        globus_l_xio_iov_max = IOV_MAX;
    }
#endif

#ifdef _XOPEN_IOV_MAX
    if (globus_l_xio_iov_max == -1)
    {
       globus_l_xio_iov_max = _XOPEN_IOV_MAX;
    }
#endif

    if (globus_l_xio_iov_max == -1)
    {
        globus_l_xio_iov_max = 16;
    }

    GlobusXIOSystemDebugExit();
    return GLOBUS_SUCCESS;

error_activate:
    GlobusXIOSystemDebugExitWithError();
    GlobusDebugDestroy(GLOBUS_XIO_SYSTEM);
    return GLOBUS_FAILURE;
}
static
void
globus_l_xio_win32_poll(
    void *                              user_arg)
{
    GlobusXIOName(globus_l_xio_win32_poll);

    GlobusXIOSystemDebugEnter();

    win32_mutex_lock(&globus_l_xio_win32_poll_lock);
    {
        if(GlobusLWin32PollQueueEmpty())
        {
            globus_reltime_t            time_left;
            
            globus_callback_get_timeout(&time_left);
            if(globus_reltime_cmp(&time_left, &globus_i_reltime_zero) > 0)
            {
                DWORD                   millis = INFINITE;
                
                if(!globus_time_reltime_is_infinity(&time_left))
                {
                    GlobusTimeReltimeToMilliSec(millis, time_left);
                }
                
                globus_l_xio_win32_poll_event_sleeping = GLOBUS_TRUE;
                win32_mutex_unlock(&globus_l_xio_win32_poll_lock);
                
                WaitForSingleObject(globus_l_xio_win32_poll_event, millis);
                
                win32_mutex_lock(&globus_l_xio_win32_poll_lock);
                globus_l_xio_win32_poll_event_sleeping = GLOBUS_FALSE;
                globus_l_xio_win32_poll_event_pending = GLOBUS_FALSE;
            }
        }
        
        while(!GlobusLWin32PollQueueEmpty())
        {
            globus_l_xio_win32_poll_entry_t * entry;
            
            GlobusLWin32PollQueueDequeue(entry);
            
            win32_mutex_unlock(&globus_l_xio_win32_poll_lock);
                
            entry->callback(entry->user_arg);
            
            win32_mutex_lock(&globus_l_xio_win32_poll_lock);
            
            entry->next = globus_l_xio_win32_poll_free;
            globus_l_xio_win32_poll_free = entry;
            
            if(globus_callback_has_time_expired())
            {
                break;
            }
        }
    }
    win32_mutex_unlock(&globus_l_xio_win32_poll_lock);

    GlobusXIOSystemDebugExit();
}
int
globus_i_xio_win32_complete_deactivate(void)
{
    globus_result_t                     result;
    globus_l_shutdown_info_t            info;
    GlobusXIOName(globus_i_xio_win32_complete_deactivate);
    
    GlobusXIOSystemDebugEnter();

    if (!globus_i_am_only_thread())
    {
        goto skip_deactivate;
    }

    globus_mutex_init(&info.mutex, NULL);
    globus_cond_init(&info.cond, NULL);
    
    globus_mutex_lock(&info.mutex);
    {
        result = globus_callback_unregister(
            globus_l_xio_win32_poll_handle,
            globus_l_xio_win32_unregister_poll_cb,
            &info,
            0);
            
        if(result == GLOBUS_SUCCESS)
        {
            /* unregister callback destroys this event object */
            while(globus_l_xio_win32_poll_event != 0)
            {
                globus_cond_wait(&info.cond, &info.mutex);
            }
        }
        else
        {
            globus_mutex_unlock(&info.mutex);
            globus_l_xio_win32_unregister_poll_cb(&info);
            globus_mutex_lock(&info.mutex);
        }
    }
    globus_mutex_unlock(&info.mutex);
    
    globus_cond_destroy(&info.cond);
    globus_mutex_destroy(&info.mutex);
    
    win32_mutex_destroy(&globus_l_xio_win32_poll_lock);
    
    while(globus_l_xio_win32_poll_free)
    {
        globus_l_xio_win32_poll_entry_t * next =
            globus_l_xio_win32_poll_free->next;
            
        globus_free(globus_l_xio_win32_poll_free);
        
        globus_l_xio_win32_poll_free = next;
    }
    
skip_deactivate:
    GlobusXIOSystemDebugExit();
    
    return GLOBUS_SUCCESS;
}