win32ole, threads and CoInitialize() / CoUninitialize()

440 views
Skip to first unread message

James Cowlishaw

unread,
Nov 26, 2010, 8:44:55 PM11/26/10
to rubyin...@googlegroups.com
Guys,

I've spotted a crash bug with Ruby 1.9.2 (and 1.9.1) using win32ole to access COM objects from within a thread. Essentially, the main thread must call CoInitialize() and CoUninitialize() if any other threads are to work with win32ole.

For example, if a rails model requires win32ole, it will not be read by the main thread under mongrel, but that of a request handler.

I've documented it here as best I can, and the fix is posted in the second gist there.

I know Ruby Installer probably isnt the best place for this, but it's the right target audience.

However, I'm new to contributing and wonder how to go about it. I'll presume I should raise an issue on http://redmine.ruby-lang.org/projects/ruby-19/issues?

I also still have to work out why Ruby 1.8.x seemed to work for me.

Thanks for taking a look and advising me on how to share.

James.

pete

unread,
Nov 27, 2010, 3:17:46 AM11/27/10
to rubyin...@googlegroups.com
> --
> You received this message because you are subscribed to the Google Groups
> "RubyInstaller" group.
> To post to this group, send email to rubyin...@googlegroups.com.
> To unsubscribe from this group, send email to
> rubyinstalle...@googlegroups.com.
> For more options, visit this group at
> http://groups.google.com/group/rubyinstaller?hl=en.
>

We discussed this a while back in regards to 1.9.1:
http://groups.google.com/group/rubyinstaller/browse_thread/thread/e5e47aabf09aff98/c328be9e67f9f391?lnk=gst&q=win32ole#c328be9e67f9f391

The verdict at the time was that it didn't affect 1.9.2. If you can
reproduce then definitely file a bug against 1.9.2 for this.

pete

pete

unread,
Nov 27, 2010, 3:20:18 AM11/27/10
to rubyin...@googlegroups.com
On Fri, Nov 26, 2010 at 5:44 PM, James Cowlishaw <ja...@cowlibob.co.uk> wrote:
> --
> You received this message because you are subscribed to the Google Groups
> "RubyInstaller" group.
> To post to this group, send email to rubyin...@googlegroups.com.
> To unsubscribe from this group, send email to
> rubyinstalle...@googlegroups.com.
> For more options, visit this group at
> http://groups.google.com/group/rubyinstaller?hl=en.
>

Also, the workaround I found was just to call CoInitialize every time
I created a thread that needed win32ole:

https://gist.github.com/407804

pete

James Cowlishaw

unread,
Nov 27, 2010, 7:17:08 AM11/27/10
to rubyin...@googlegroups.com

Yes, I saw both those. The problem here is that if three threads (1, 2 & 3) call CoInitialize() in that order, then thread 1 must call CoUnintialize() last of all, after all other COM activity has finished.

The effect for me was an access violation during ole_free, triggered in the GC.

James.

Luis Lavena

unread,
Nov 27, 2010, 11:37:42 AM11/27/10
to rubyin...@googlegroups.com
On Sat, Nov 27, 2010 at 9:17 AM, James Cowlishaw <cowl...@gmail.com> wrote:
> Yes, I saw both those. The problem here is that if three threads (1, 2 & 3) call CoInitialize() in that order, then thread 1 must call CoUnintialize() last of all, after all other COM activity has finished.
>
> The effect for me was an access violation during ole_free, triggered in the GC.

I would ask you to check against ruby trunk codebase. To be able to
test this will require the following:

1) Use Git to checkout ruby:

git clone git://github.com/ruby/ruby.git

That will put the trunk (1.9.3) branch as default. It will take a few
minutes to download (Ruby is big)

2) Checkout rubyinstaller

git clone git://github.com/oneclick/rubyinstaller.git

3) Use rake to buidl Ruby source:

cd rubyinstaller

rake ruby19 LOCAL=C:\Path\To\Cloned\Ruby

4) Test using the sandbox environment:

cd sandbox\ruby19_mingw
set PATH=%CD%\bin;%PATH%
ruby -v

5) Run your script and report back.

Regards,
--
Luis Lavena
AREA 17
-
Perfection in design is achieved not when there is nothing more to add,
but rather when there is nothing more to take away.
Antoine de Saint-Exupéry

James Cowlishaw

unread,
Nov 27, 2010, 11:53:10 AM11/27/10
to rubyin...@googlegroups.com
Thanks for the advice,

I'll run through it this weekend.

James.


--

Luis Lavena

unread,
Nov 27, 2010, 3:31:55 PM11/27/10
to rubyin...@googlegroups.com
On Sat, Nov 27, 2010 at 1:53 PM, James Cowlishaw <ja...@securstar.com> wrote:
> Thanks for the advice,
> I'll run through it this weekend.

Cool, and for the record, I reported this to Ruby-Core long ago:

http://redmine.ruby-lang.org/issues/show/2618

The issue described there seems to work for me with 1.9.2-p0

Please let me know so I can either update the ticket or mark it as close.

Thank you.

Octagon

unread,
Nov 28, 2010, 3:56:15 AM11/28/10
to RubyInstaller
On Nov 27, 4:44 am, James Cowlishaw <ja...@cowlibob.co.uk> wrote:
> Guys,
>
> I've spotted a crash bug with Ruby 1.9.2 (and 1.9.1) using win32ole to
> access COM objects from within a thread. Essentially, the main thread must
> call CoInitialize() and CoUninitialize() if any other threads are to work
> with win32ole.
>

Just in case.

Each Windows thread must call CoInitialize() and CoUninitialize().
These calls
are dirt cheap, especially starting with the second one. So, if any
block of code
does not make these calls, it is a bug.

Ruby 1.8 threads are emulated within the interpreter and there is only
one Windows
thread. Thus, it makes no difference where CoInitialized() is called.
In Ruby 1.9
native threads are (should be? must be? may be?) used.

Practically, this means that the main thread must be aware that any
Windows
thread it launches uses COM and must call CoInitialize() before that
thread
is launched. As for CoUninitialized(), Windows can work for a while if
it is not
called.

Thus, "Essentially, the main thread must call CoInitialize() and
CoUninitialize()
if any other threads are to work with win32ole. " is absolutely
correct and I do
not see any problem. Welcome to Windows.

Octagon

unread,
Nov 28, 2010, 4:12:40 AM11/28/10
to RubyInstaller
On Nov 27, 4:44 am, James Cowlishaw <ja...@cowlibob.co.uk> wrote:
> I've documented it
> here<http://jcowlishaw.posterous.com/ruby-threads-win32ole-coinitialize-an...>
> as
> best I can, and the fix is posted in the second
> gist<https://gist.github.com/717446.js?file=win32ole.rb>
>  there.

Forgot one thing, sorry.

I do not know Ruby well enough, but the fix in
http://jcowlishaw.posterous.com/ruby-threads-win32ole-coinitialize-and-counin
looks suspicious to me. Each thread must make its own CoInitialize()
call, and apparently the fix prevents that. Will be glad to be wrong.

James Cowlishaw

unread,
Nov 28, 2010, 4:25:45 AM11/28/10
to rubyin...@googlegroups.com

No, the Thread.main check (and the Thread block method monkey patch) will only be evaluated at the time of the 1st require.

I suspect this would prevent the use of " load 'win32ole' " though.

Thanks,
James.

James Cowlishaw

unread,
Nov 28, 2010, 4:32:03 AM11/28/10
to rubyin...@googlegroups.com

Thankyou,

You've pointed out the reason I had no trouble running against 1.8.7 builds (non-native threads).

James.
>

Reply all
Reply to author
Forward
0 new messages