From 64c184b8bbd303e0069d66a76aa64d68846f47a0 Mon Sep 17 00:00:00 2001 From: Jameson Nash Date: Fri, 24 Jul 2015 00:34:26 -0400 Subject: [PATCH] provide GetLastError/FormatMessage as official functions of the [Windows] Libc module (also use them to provide better errors in the tests) these belong in Libc since the are the windows counterparts to errno/strerror which are documented and exported in Libc --- base/deprecated.jl | 4 ++-- base/env.jl | 27 ++++--------------------- base/file.jl | 4 ++-- base/libc.jl | 23 ++++++++++++++++++++++ base/mmap.jl | 10 +++++----- doc/stdlib/libc.rst | 10 +++++++++- src/support/wsasocketpair.c | 39 ++++++++++++++++++++++++------------- test/pollfd.jl | 33 +++++++++++++++---------------- 8 files changed, 87 insertions(+), 63 deletions(-) diff --git a/base/deprecated.jl b/base/deprecated.jl index dad7aac7da3f9..cf69c151a8335 100644 --- a/base/deprecated.jl +++ b/base/deprecated.jl @@ -606,7 +606,7 @@ function munmap(viewhandle::Ptr, mmaphandle::Ptr) status = ccall(:UnmapViewOfFile, stdcall, Cint, (Ptr{Void},), viewhandle)!=0 status |= ccall(:CloseHandle, stdcall, Cint, (Ptr{Void},), mmaphandle)!=0 if !status - error("could not unmap view: $(FormatMessage())") + error("could not unmap view: $(Libc.FormatMessage())") end end @@ -614,7 +614,7 @@ function msync(p::Ptr, len::Integer) depwarn("`msync` is deprecated, use `Mmap.sync!(array)` instead", :msync) status = ccall(:FlushViewOfFile, stdcall, Cint, (Ptr{Void}, Csize_t), p, len)!=0 if !status - error("could not msync: $(FormatMessage())") + error("could not msync: $(Libc.FormatMessage())") end end diff --git a/base/env.jl b/base/env.jl index c82cd267ecbce..f5ef277b6d36c 100644 --- a/base/env.jl +++ b/base/env.jl @@ -4,35 +4,16 @@ _getenv(var::AbstractString) = ccall(:getenv, Ptr{UInt8}, (Cstring,), var) _hasenv(s::AbstractString) = _getenv(s) != C_NULL end + @windows_only begin const ERROR_ENVVAR_NOT_FOUND = UInt32(203) -const FORMAT_MESSAGE_ALLOCATE_BUFFER = UInt32(0x100) -const FORMAT_MESSAGE_FROM_SYSTEM = UInt32(0x1000) -const FORMAT_MESSAGE_IGNORE_INSERTS = UInt32(0x200) -const FORMAT_MESSAGE_MAX_WIDTH_MASK = UInt32(0xFF) -GetLastError() = ccall(:GetLastError,stdcall,UInt32,()) -function FormatMessage(e=GetLastError()) - lpMsgBuf = Array(Ptr{UInt16}) - lpMsgBuf[1] = 0 - len = ccall(:FormatMessageW,stdcall,UInt32,(Cint, Ptr{Void}, Cint, Cint, Ptr{Ptr{UInt16}}, Cint, Ptr{Void}), - FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS | FORMAT_MESSAGE_MAX_WIDTH_MASK, - C_NULL, e, 0, lpMsgBuf, 0, C_NULL) - p = lpMsgBuf[1] - len == 0 && return utf8("") - len = len + 1 - buf = Array(UInt16, len) - unsafe_copy!(pointer(buf), p, len) - ccall(:LocalFree,stdcall,Ptr{Void},(Ptr{Void},),p) - return utf8(UTF16String(buf)) -end - _getenvlen(var::AbstractString) = ccall(:GetEnvironmentVariableW,stdcall,UInt32,(Cwstring,Ptr{UInt8},UInt32),var,C_NULL,0) -_hasenv(s::AbstractString) = _getenvlen(s)!=0 || GetLastError()!=ERROR_ENVVAR_NOT_FOUND +_hasenv(s::AbstractString) = _getenvlen(s)!=0 || Libc.GetLastError()!=ERROR_ENVVAR_NOT_FOUND function _jl_win_getenv(s::UTF16String,len::UInt32) val=zeros(UInt16,len) ret=ccall(:GetEnvironmentVariableW,stdcall,UInt32,(Cwstring,Ptr{UInt16},UInt32),s,val,len) if (ret == 0 && len != 1) || ret != len-1 || val[end] != 0 - error(string("getenv: ", s, ' ', len, "-1 != ", ret, ": ", FormatMessage())) + error(string("getenv: ", s, ' ', len, "-1 != ", ret, ": ", Libc.FormatMessage())) end val end @@ -50,7 +31,7 @@ macro accessEnv(var,errorcase) let var = utf16($(esc(var))) len=_getenvlen(var) if len == 0 - if GetLastError() != ERROR_ENVVAR_NOT_FOUND + if Libc.GetLastError() != ERROR_ENVVAR_NOT_FOUND return utf8("") else $(esc(errorcase)) diff --git a/base/file.jl b/base/file.jl index 2d3c1de5447a6..6b1d45f49a260 100644 --- a/base/file.jl +++ b/base/file.jl @@ -175,7 +175,7 @@ function tempdir() temppath = Array(UInt16,32767) lentemppath = ccall(:GetTempPathW,stdcall,UInt32,(UInt32,Ptr{UInt16}),length(temppath),temppath) if lentemppath >= length(temppath) || lentemppath == 0 - error("GetTempPath failed: $(FormatMessage())") + error("GetTempPath failed: $(Libc.FormatMessage())") end resize!(temppath,lentemppath+1) return utf8(UTF16String(temppath)) @@ -186,7 +186,7 @@ function tempname(temppath::AbstractString,uunique::UInt32) uunique = ccall(:GetTempFileNameW,stdcall,UInt32,(Cwstring,Ptr{UInt16},UInt32,Ptr{UInt16}), temppath,utf16("jul"),uunique,tname) lentname = findfirst(tname,0)-1 if uunique == 0 || lentname <= 0 - error("GetTempFileName failed: $(FormatMessage())") + error("GetTempFileName failed: $(Libc.FormatMessage())") end resize!(tname,lentname+1) return utf8(UTF16String(tname)) diff --git a/base/libc.jl b/base/libc.jl index 4edb24ebca156..caf650a885d15 100644 --- a/base/libc.jl +++ b/base/libc.jl @@ -4,6 +4,7 @@ module Libc export FILE, TmStruct, strftime, strptime, getpid, gethostname, free, malloc, calloc, realloc, errno, strerror, flush_cstdio, systemsleep, time +@windows_only export GetLastError, FormatMessage include("errno.jl") @@ -165,6 +166,28 @@ errno(e::Integer) = ccall(:jl_set_errno, Void, (Cint,), e) strerror(e::Integer) = bytestring(ccall(:strerror, Ptr{UInt8}, (Int32,), e)) strerror() = strerror(errno()) +@windows_only begin +GetLastError() = ccall(:GetLastError,stdcall,UInt32,()) +function FormatMessage(e=GetLastError()) + const FORMAT_MESSAGE_ALLOCATE_BUFFER = UInt32(0x100) + const FORMAT_MESSAGE_FROM_SYSTEM = UInt32(0x1000) + const FORMAT_MESSAGE_IGNORE_INSERTS = UInt32(0x200) + const FORMAT_MESSAGE_MAX_WIDTH_MASK = UInt32(0xFF) + lpMsgBuf = Array(Ptr{UInt16}) + lpMsgBuf[1] = 0 + len = ccall(:FormatMessageW,stdcall,UInt32,(Cint, Ptr{Void}, Cint, Cint, Ptr{Ptr{UInt16}}, Cint, Ptr{Void}), + FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS | FORMAT_MESSAGE_MAX_WIDTH_MASK, + C_NULL, e, 0, lpMsgBuf, 0, C_NULL) + p = lpMsgBuf[1] + len == 0 && return utf8("") + len = len + 1 + buf = Array(UInt16, len) + unsafe_copy!(pointer(buf), p, len) + ccall(:LocalFree,stdcall,Ptr{Void},(Ptr{Void},),p) + return utf8(UTF16String(buf)) +end +end + ## Memory related ## free(p::Ptr) = ccall(:free, Void, (Ptr{Void},), p) diff --git a/base/mmap.jl b/base/mmap.jl index d1961546b6e6f..b1de73dec7224 100644 --- a/base/mmap.jl +++ b/base/mmap.jl @@ -81,7 +81,7 @@ const FILE_MAP_EXECUTE = UInt32(0x20) function gethandle(io::IO) handle = Libc._get_osfhandle(RawFD(fd(io))).handle - systemerror("could not get handle for file to map: $(Base.FormatMessage())", handle == -1) + systemerror("could not get handle for file to map: $(Libc.FormatMessage())", handle == -1) return Int(handle) end @@ -127,10 +127,10 @@ function mmap{T,N}(io::IO, file_desc, C_NULL, readonly ? PAGE_READONLY : PAGE_READWRITE, szfile >> 32, szfile & typemax(UInt32), name) : ccall(:OpenFileMappingW, stdcall, Ptr{Void}, (Cint, Cint, Cwstring), readonly ? FILE_MAP_READ : FILE_MAP_WRITE, true, name) - handle == C_NULL && error("could not create file mapping: $(Base.FormatMessage())") + handle == C_NULL && error("could not create file mapping: $(Libc.FormatMessage())") ptr = ccall(:MapViewOfFile, stdcall, Ptr{Void}, (Ptr{Void}, Cint, Cint, Cint, Csize_t), handle, readonly ? FILE_MAP_READ : FILE_MAP_WRITE, offset_page >> 32, offset_page & typemax(UInt32), (offset - offset_page) + len) - ptr == C_NULL && error("could not create mapping view: $(Base.FormatMessage())") + ptr == C_NULL && error("could not create mapping view: $(Libc.FormatMessage())") end # @windows_only # convert mmapped region to Julia Array at `ptr + (offset - offset_page)` since file was mapped at offset_page A = pointer_to_array(convert(Ptr{T}, UInt(ptr) + UInt(offset - offset_page)), dims) @@ -138,7 +138,7 @@ function mmap{T,N}(io::IO, @windows_only finalizer(A, x -> begin status = ccall(:UnmapViewOfFile, stdcall, Cint, (Ptr{Void},), ptr)!=0 status |= ccall(:CloseHandle, stdcall, Cint, (Ptr{Void},), handle)!=0 - status || error("could not unmap view: $(Base.FormatMessage())") + status || error("could not unmap view: $(Libc.FormatMessage())") end) return A end @@ -202,7 +202,7 @@ const MS_SYNC = 4 function sync!{T}(m::Array{T}, flags::Integer=MS_SYNC) @unix_only systemerror("msync", ccall(:msync, Cint, (Ptr{Void}, Csize_t, Cint), pointer(m), length(m)*sizeof(T), flags) != 0) - @windows_only systemerror("could not FlushViewOfFile: $(Base.FormatMessage())", + @windows_only systemerror("could not FlushViewOfFile: $(Libc.FormatMessage())", ccall(:FlushViewOfFile, stdcall, Cint, (Ptr{Void}, Csize_t), pointer(m), length(m)) == 0) end sync!(B::BitArray, flags::Integer=MS_SYNC) = sync!(B.chunks, flags) diff --git a/doc/stdlib/libc.rst b/doc/stdlib/libc.rst index 12d8a6f33d562..959be71ab3e35 100644 --- a/doc/stdlib/libc.rst +++ b/doc/stdlib/libc.rst @@ -34,10 +34,18 @@ library routine that sets it. Specifically, you cannot call ``errno`` at the next prompt in a REPL, because lots of code is executed between prompts. -.. function:: strerror(n) +.. function:: strerror(n=errno()) Convert a system call error code to a descriptive string +.. function:: GetLastError() + + Call the Win32 ``GetLastError`` function [only available on Windows]. + +.. function:: FormatMessage(n=GetLastError()) + + Convert a Win32 system call error code to a descriptive string [only available on Windows]. + .. function:: time(t::TmStruct) Converts a ``TmStruct`` struct to a number of seconds since the epoch. diff --git a/src/support/wsasocketpair.c b/src/support/wsasocketpair.c index f77ea13640d60..72cf35daed8ab 100644 --- a/src/support/wsasocketpair.c +++ b/src/support/wsasocketpair.c @@ -29,41 +29,54 @@ __declspec(dllexport) int wsasocketpair(int domain, int type, int protocol, SOCK return -1; } name.sin_family = AF_INET; - name.sin_addr.s_addr = INADDR_LOOPBACK; + name.sin_addr.s_addr = htonl(INADDR_LOOPBACK); name.sin_port = 0; err = bind(server, (SOCKADDR*)&name, sizeof(name)); if (err == SOCKET_ERROR) { - closesocket(server); - return -1; + int lasterr = WSAGetLastError(); + closesocket(server); + WSASetLastError(lasterr); + return -1; } err = listen(server, 1); if (err == SOCKET_ERROR) { - closesocket(server); - return -1; + int lasterr = WSAGetLastError(); + closesocket(server); + WSASetLastError(lasterr); + return -1; } namelen = sizeof(name); err = getsockname(server, (SOCKADDR*)&name, &namelen); if (err == SOCKET_ERROR) { - closesocket(server); - return -1; + int lasterr = WSAGetLastError(); + closesocket(server); + WSASetLastError(lasterr); + return -1; } client0 = socket(AF_INET, type, protocol); if (client0 == INVALID_SOCKET) { + int lasterr = WSAGetLastError(); closesocket(server); + WSASetLastError(lasterr); return -1; } err = connect(client0, (SOCKADDR*)&name, sizeof(name)); if (err == SOCKET_ERROR) { - closesocket(client0); - closesocket(server); - return -1; + int lasterr = WSAGetLastError(); + closesocket(client0); + closesocket(server); + WSASetLastError(lasterr); + return -1; } client1 = accept(server, NULL, NULL); - closesocket(server); if (err == INVALID_SOCKET) { - closesocket(client0); - return -1; + int lasterr = WSAGetLastError(); + closesocket(server); + closesocket(client0); + WSASetLastError(lasterr); + return -1; } + closesocket(server); socket_vector[0] = client0; socket_vector[1] = client1; return 0; diff --git a/test/pollfd.jl b/test/pollfd.jl index ccc9babbdc54b..cce7f5c9cd044 100644 --- a/test/pollfd.jl +++ b/test/pollfd.jl @@ -13,14 +13,13 @@ intvls = [2, .2, .1, .002] pipe_fds = cell(n) for i in 1:n - @test 0 == - @windows ? begin - pipe_fds[i] = Array(Libc.WindowsRawSocket, 2) - ccall(:wsasocketpair, Cint, (Cint, Cuint, Cint, Ptr{Libc.WindowsRawSocket}), 1, 1, 6, pipe_fds[i]) - end : begin - pipe_fds[i] = Array(RawFD, 2) - ccall(:pipe, Cint, (Ptr{RawFD},), pipe_fds[i]) - end + @windows ? begin + pipe_fds[i] = Array(Libc.WindowsRawSocket, 2) + 0 == ccall(:wsasocketpair, Cint, (Cint, Cuint, Cint, Ptr{Libc.WindowsRawSocket}), 1, 1, 6, pipe_fds[i]) || error(Libc.FormatMessage()) + end : begin + pipe_fds[i] = Array(RawFD, 2) + @test 0 == ccall(:pipe, Cint, (Ptr{RawFD},), pipe_fds[i]) + end end function pfd_tst_reads(idx, intvl) @@ -38,10 +37,10 @@ function pfd_tst_reads(idx, intvl) @test t_elapsed <= (intvl + 1) dout = Array(UInt8, 1) - @test 1 == @windows ? ( - ccall(:recv, stdcall, Cint, (Ptr{Void}, Ptr{UInt8}, Cint, Cint), pipe_fds[idx][1], dout, 1, 0) + @windows ? ( + 1 == ccall(:recv, stdcall, Cint, (Ptr{Void}, Ptr{UInt8}, Cint, Cint), pipe_fds[idx][1], dout, 1, 0) || error(Libc.FormatMessage()) ) : ( - ccall(:read, Csize_t, (Cint, Ptr{UInt8}, Csize_t), pipe_fds[idx][1], dout, 1) + @test 1 == ccall(:read, Csize_t, (Cint, Ptr{UInt8}, Csize_t), pipe_fds[idx][1], dout, 1) ) @test dout[1] == Int8('A') end @@ -87,10 +86,10 @@ for (i, intvl) in enumerate(intvls) @test event.writable if isodd(idx) - @test 1 == @windows ? ( - ccall(:send, stdcall, Cint, (Ptr{Void}, Ptr{UInt8}, Cint, Cint), pipe_fds[idx][2], "A", 1, 0) + @windows ? ( + 1 == ccall(:send, stdcall, Cint, (Ptr{Void}, Ptr{UInt8}, Cint, Cint), pipe_fds[idx][2], "A", 1, 0) || error(Libc.FormatMessage()) ) : ( - ccall(:write, Csize_t, (Cint, Ptr{UInt8}, Csize_t), pipe_fds[idx][2], "A", 1) + @test 1 == ccall(:write, Csize_t, (Cint, Ptr{UInt8}, Csize_t), pipe_fds[idx][2], "A", 1) ) end end @@ -100,10 +99,10 @@ end for i in 1:n for j = 1:2 - @test 0 == @windows ? ( - ccall(:closesocket, stdcall, Cint, (Ptr{Void},), pipe_fds[i][j]) + @windows ? ( + 0 == ccall(:closesocket, stdcall, Cint, (Ptr{Void},), pipe_fds[i][j]) || error(Libc.FormatMessage()) ) : ( - ccall(:close, Cint, (Cint,), pipe_fds[i][j]) + @test 0 == ccall(:close, Cint, (Cint,), pipe_fds[i][j]) ) end end