[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