Skip to content

The first *win32PipeListener.Accept will cause the client hanged in Windows 7 #173

@mi1eschen

Description

@mi1eschen

I was trying go-winio in my project, but I noticed that the same code had different results in Win 7 and Win 10. In Win 7, the client was hanged on first connecting after server restarted, and worked correctly after the second time. After debugged and found that server's syscall.getQueuedCompletionStatus couldn't be triggered when the client first connected. Then I tried to find the reason and noticed that win32PipeListener.firstHandle's access was syscall.SYNCHRONIZE. I found they created named pipe with full access in the loop in this example, so I tried to modify code and the bellow is the diff block.

diff --git a/pipe.go b/pipe.go                                                                      
index d6a46f6..ac59ca0 100644                                                                       
--- a/pipe.go                                                                                       
+++ b/pipe.go                                                                                       
@@ -259,6 +259,7 @@ type acceptResponse struct {                                                    
                                                                                                    
 type win32PipeListener struct {                                                                    
        firstHandle syscall.Handle                                                                  
+       handle      syscall.Handle                                                                  
        path        string                                                                          
        config      PipeConfig                                                                      
        acceptCh    chan (chan acceptResponse)                                                      
@@ -319,7 +320,7 @@ func makeServerPipeHandle(path string, sd []byte, c *PipeConfig, first bool) (sy
                // By not asking for read or write access, the named pipe file system               
                // will put this pipe into an initially disconnected state, blocking                
                // client connections until the next call with first == false.                      
-               access = syscall.SYNCHRONIZE                                                        
+               // access = syscall.SYNCHRONIZE                                                     
        }                                                                                           
                                                                                                    
        timeout := int64(-50 * 10000) // 50ms
@@ -338,13 +339,19 @@ func makeServerPipeHandle(path string, sd []byte, c *PipeConfig, first bool) (sy
 }                                                                                                    
                                                                                                      
 func (l *win32PipeListener) makeServerPipe() (*win32File, error) {                                   
-       h, err := makeServerPipeHandle(l.path, nil, &l.config, false)                                 
-       if err != nil {                                                                               
-               return nil, err                                                                       
+       handle := l.handle                                                                            
+       if handle == 0 {                                                                              
+               h, err := makeServerPipeHandle(l.path, nil, &l.config, false)                         
+               if err != nil {                                                                       
+                       return nil, err                                                               
+               }                                                                                     
+               handle = h                                                                            
+       } else {                                                                                      
+               l.handle = 0                                                                          
        }                                                                                             
-       f, err := makeWin32File(h)                                                                    
+       f, err := makeWin32File(handle)                                                               
        if err != nil {                                                                               
-               syscall.Close(h)                                                                      
+               syscall.Close(handle)                                                                 
                return nil, err                                                                       
        }                                                                                             
        return f, nil                                                                                 
@@ -451,6 +458,7 @@ func ListenPipe(path string, c *PipeConfig) (net.Listener, error) {               
        }                                                                                             
        l := &win32PipeListener{                                                                      
                firstHandle: h,                                                                       
+               handle:      h,                                                                       
                path:        path,                                                                    
                config:      *c,                                                                      
                acceptCh:    make(chan (chan acceptResponse)),                                        

Then it worked on both Win 7 and Win 10!

Here is my test code. link

Actually, I think the current implementation is correct, and I still don't know why it fails on Win 7.

Sorry for poor English...

Test Environment:
OS: Windows 7 ( 7601: service pack 1 )

Build Environment:
OS: Windows 10 (1909)
GO version: 1.14 windows/amd64

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions