[Win32utils-devel] Some more win32-changenotify analysis

Heesob Park phasis at gmail.com
Wed Aug 8 11:36:43 EDT 2007


Hi,

2007/8/8, Daniel Berger <djberg96 at gmail.com>:
>
> Hi all,
>
> I decided to check the responsiveness of the pure Ruby vs C extension
> versions of win32-changenotify. I setup this little file generator
> program:
>
> a = []
>
> 10.times{ |n|
>    a << Thread.new{
>       File.open("File_#{n}", 'w'){ |fh| fh.puts "test #{n}" }
>    }
> }
>
> a.each{ |t| t.join }
>
> The pure Ruby version did not do so well. In some cases it only picked
> up one event. In most cases, it didn't pick up *any* events!
>
> The C extension did much better, though the behavior was somewhat odd.
> Most of the time it picked up multiple notifications on some files,
> while missing others entirely:
>
> [#<struct Struct::ChangeNotifyStruct action="modified",
> file_name="test1\\test2\\File_0">]
> [#<struct Struct::ChangeNotifyStruct action="modified",
> file_name="test1\\test2\\File_4">]
> [#<struct Struct::ChangeNotifyStruct action="modified",
> file_name="test1\\test2\\File_4">]
> [#<struct Struct::ChangeNotifyStruct action="modified",
> file_name="test1\\test2\\File_5">]
> [#<struct Struct::ChangeNotifyStruct action="modified",
> file_name="test1\\test2\\File_5">]
> [#<struct Struct::ChangeNotifyStruct action="modified",
> file_name="test1\\test2\\File_6">]
> [#<struct Struct::ChangeNotifyStruct action="modified",
> file_name="test1\\test2\\File_6">]
> [#<struct Struct::ChangeNotifyStruct action="modified",
> file_name="test1\\test2\\File_7">]
> [#<struct Struct::ChangeNotifyStruct action="modified",
> file_name="test1\\test2\\File_7">]
> [#<struct Struct::ChangeNotifyStruct action="modified",
> file_name="test1\\test2\\File_8">]
> [#<struct Struct::ChangeNotifyStruct action="modified",
> file_name="test1\\test2\\File_8">]
> [#<struct Struct::ChangeNotifyStruct action="modified",
> file_name="test1\\test2\\File_9">]
> [#<struct Struct::ChangeNotifyStruct action="modified",
> file_name="test1\\test2\\File_9">]
>
> I also tried with a C version of win32-changenotify using a pure Ruby
> version of win32-ipc. Although the results were mixed, it did seem to
> get a little worse in terms of the notifications it returned, typically
> only returning 4 or 5 notifications, though sometimes it did as well or
> better than using the C version of win32-ipc, so I'm not sure what to
> make of that.
>
> I'm not sure what to make of the fact that the pure Ruby version doesn't
> pick up Threaded events most (all?) of the time. Maybe there's some way
> to use threads within changenotify.rb to make it more responsive?
>
> Otherwise, I think I'll have to declare this a failed experiment.
>
> Regards,
>
> Dan


Don't give up yet :)

I googled and found a Delphi version.
I ported it into Ruby and in my test, it can catch all events.

It requires CreateIoCompletionPort  and GetQueuedCompletionStatus API
function.
I also found a bug releated with aquiring next offset of fni at
get_file_action.

Here is another version of wait and get_file_action:

      CreateIoCompletionPort = Win32API.new('kernel32',
'CreateIoCompletionPort', 'LLPL', 'L')
      GetQueuedCompletionStatus = Win32API.new('kernel32',
'GetQueuedCompletionStatus', 'LPPPL', 'I')

      def CreateIoCompletionPort(handle,port,key,threads)
        CreateIoCompletionPort.call(handle,port,key,threads)
      end
      def GetQueuedCompletionStatus(handle,bytes,key,overlap,millisec)
       GetQueuedCompletionStatus.call(handle,bytes,key,overlap,millisec) > 0
      end

      # Waits up to 'seconds' for a notification to occur, or infinitely
      # if no value is specified.
      #
      # If a block is provided, yields a ChangeNotifyStruct that contains
two
      # members: file_name and action.
      #
      def wait(seconds = INFINITE)
         seconds *= 1000 unless seconds == INFINITE

         fni   = 0.chr * 65536 # FILE_NOTIFY_INFORMATION struct buffer
         bytes = [0].pack('L')
         num_bytes = [0].pack('L')

         subtree    = @recursive ? 1 : 0
         dir_handle = get_dir_handle(@path)

         completion_key = [12345].pack('L')
         completion_port = CreateIoCompletionPort(dir_handle, 0,
completion_key, 0);

         bool = ReadDirectoryChangesW(dir_handle,fni,fni.size
,subtree, at filter,bytes, at overlap,0)
         unless bool
            raise Error, get_last_error
         end

         while true
           GetQueuedCompletionStatus(completion_port, num_bytes,
completion_key, @overlap, seconds)
           break if completion_key.unpack('L').first == 0
           yield get_file_action(fni) if block_given?
           ReadDirectoryChangesW(dir_handle,fni,fni.size
,subtree, at filter,bytes, at overlap,0)
        end
      end


      def get_file_action(fni2)
         fni = fni2.dup
         array  = []

         while true
            int_action = fni[4,4].unpack('L')[0]

            str_action = 'unknown'

            case int_action
               when FILE_ACTION_ADDED
                  str_action = 'added'
               when FILE_ACTION_REMOVED
                  str_action = 'removed'
               when FILE_ACTION_MODIFIED
                  str_action = 'modified'
               when FILE_ACTION_RENAMED_OLD_NAME
                  str_action = 'renamed old name'
               when FILE_ACTION_RENAMED_NEW_NAME
                  str_action = 'renamed new name'
            end

            len  = fni[8,4].unpack('L').first # FileNameLength struct member
            file = fni[12,len] + "\0\0"       # FileName struct member +
null
            buf  = 0.chr * 260

            WideCharToMultiByte(CP_ACP, 0, file, -1, buf, 260, 0, 0)

            file = File.join(@path, buf.unpack('A*')[0])

            struct = ChangeNotifyStruct.new(str_action, file)
            array.push(struct)
            break if fni[0,4].unpack('L')[0] == 0
            fni = fni[fni[0,4].unpack('L').first .. -1] # Next offset
            break if fni.nil?
         end

         array
      end
-------------- next part --------------
An HTML attachment was scrubbed...
URL: http://rubyforge.org/pipermail/win32utils-devel/attachments/20070809/1b1b6e5e/attachment.html 


More information about the win32utils-devel mailing list