Ruby and CLR interactions

While building my Ruby <-> CLR shim, I came across a nasty issue with
respect to initializing the CLR in a delay-load scenario.

In existing versions of the CLR (< 2.0), COM is not guaranteed to be
initialized. Most of the time it isn't, which is why all of the existing
Ruby <-> CLR bridges can call CoInitializeEx via
Thread.ApartmentStatewithout potentially breaking something. However,
there are some (rare)
scenarios where the CLR will initialize COM prior to the Ruby bridge being
called, which will hose folks who were attempting to change the
ApartmentState in their Ruby scripts (or in their bridge).

The Whidbey (V2.0) version of the CLR forces all threads into the MTA by
default - this gets rid of the indeterminate behavior of earlier versions of
the CLR. This currently will hose anyone using any of the existing bridges,
unless you jump through some exotic hoops. Once I release the source for my
bridge, you'll see what those exotic hoops look like. If any of the existing
bridge maintainers want to know the specifics, please feel free to ask away.

Why would someone need to specify a COM threading model? If, for example you
want to create a Windows Forms application or an Avalon application, you'll
need to have your thread running in its own STA. But if I don't initialize
the CLR until after my Ruby program has already started running, I'm going
to run into some nasty corner cases. For example, what if someone called a
COM object via win32ole? By the time we get to my shim, COM will have been
initialized already and there's not a lot I can do other than fail when I
attempt to initialize COM again.

What would be better is to declare my COM threading requirements at startup
time for my Ruby program. Now, I really don't know Ruby all that well, so I
was wondering if folks would have some ideas about *how* I can declare my
threading requirements at startup time? In a C# program, I can do this:

[STAThread]
public static void Main() { ... }

The CLR will guarantee that the thread that calls into Main() will live in
its own STA if it sees a [STAThread] attribute on the Main() method.

Now, I don't really know Ruby all that well - I've only been using this
language for a few months. I was wondering if the community might have some
suggestions about how I might approach this problem. Perhaps some kind of
platform-specific runtime initialization file? For example, if you start a
foo.rb, that the Ruby runtime looks for the presence of a foo.rb.config file
to grab some startup configuration?

BTW, I'm going to be attending RubyConf this year, so if anyone wants to get
together to talk about Ruby <-> CLR integration, I'd be more than happy to
talk to you. I'm staying over for OOPSLA as well, so there should be ample
time to hack out a good solution to this problem :slight_smile:

Thanks,
-John

Hi John,
I'm really glad to see that someone is working on a shim for CLR 2.0!

But if I don't initialize
the CLR until after my Ruby program has already started running, I'm

going

to run into some nasty corner cases. For example, what if someone called

a

COM object via win32ole? By the time we get to my shim, COM will have

been

initialized already and there's not a lot I can do other than fail when I
attempt to initialize COM again.

I was wondering if folks would have some ideas about *how* I can declare

my

threading requirements at startup time?

I'm not sure I'm clear on when you need to declare the threading
requirements.
Would doing something like this as the first 2 lines in the .rb file work?
require 'clrshim'
setSTA
where 'setSTA' (or 'setMTA') is a method your shim defines.
as long as any win32ole stuff came after this, we should be OK.
Or am I overlooking something?
Wayne Vucenic
No Bugs Software
"Ruby and C++ Agile Contract Programming in Silicon Valley"

Hi Wayne,

I'm really glad to see that someone is working on a shim for CLR 2.0!

Me too :slight_smile:

Would doing something like this as the first 2 lines in the .rb file

work?

require 'clrshim'
setSTA

The problem is that require 'rubyshim' would have to be guaranteed to be the
first line of code in a *program*. I'm not sure at all how I can make that
guarantee given the semantics of require.

So hence my thinking around doing a platform-specific change to the Ruby
runtime to check for a configuration file.

-John

Hi John,
I think I understand your concern.
Suppose I have a file called clrstuff.rb, which uses the CLR shim. The
apartment model gets set up correctly, and everything works fine.
Now someone writes a file, foo.rb, which uses win32ole, then does a
"require 'clrstuff'". But win32ole may have set up an incompatible apartment
model before 'clrstuff' gets invoked, which causes problems for 'clrstuff'.
Is this basically the problem?
I believe your proposal is to put some info on the required configuration
into foo.rb.config. I assume you're proposing a separate file to handle the
case where we're unwilling/unable to simply make the changes directly to
foo.rb.
It seems like this would work, and I can't right off think of a
substantially different approach. It's unfortunate that the configuration
file needs to be attached to foo.rb rather than to clrstuff.rb. This way
when the file bar.rb comes along which uses win32ole and then requires '
foo.rb', we'll also need a bar.rb.config.
I'd like to suggest a slight modification/generalization to your proposal.
I'd suggest supporting two files, with names like foo.rb.pre and foo.rb.post.
Both files contain ordinary Ruby code, but ".pre" is run before foo.rb and
".post" is run after. .pre can be used to do what your .config file would
do. .post would be useful when foo.rb contains a buggy method and for
whatever reason I can't change foo.rb. Then I can redefine the problematic
method in .post.
Wayne

···

On 9/26/05, John Lam <drjflam@gmail.com> wrote:

Hi Wayne,

>> I'm really glad to see that someone is working on a shim for CLR 2.0!

Me too :slight_smile:

>> Would doing something like this as the first 2 lines in the .rb file
work?
>> require 'clrshim'
>> setSTA

The problem is that require 'rubyshim' would have to be guaranteed to be
the
first line of code in a *program*. I'm not sure at all how I can make that
guarantee given the semantics of require.

So hence my thinking around doing a platform-specific change to the Ruby
runtime to check for a configuration file.

-John

Hi John,
I think I understand your concern.
Suppose I have a file called clrstuff.rb, which uses the CLR shim. The
apartment model gets set up correctly, and everything works fine.
Now someone writes a file, foo.rb, which uses win32ole, then does a
"require 'clrstuff'". But win32ole may have set up an incompatible
apartment
model before 'clrstuff' gets invoked, which causes problems for
'clrstuff'.
Is this basically the problem?
I believe your proposal is to put some info on the required configuration
into foo.rb.config. I assume you're proposing a separate file to handle
the
case where we're unwilling/unable to simply make the changes directly to
foo.rb.
It seems like this would work, and I can't right off think of a
substantially different approach. It's unfortunate that the configuration
file needs to be attached to foo.rb rather than to clrstuff.rb. This way
when the file bar.rb comes along which uses win32ole and then requires '
foo.rb', we'll also need a bar.rb.config.
I'd like to suggest a slight modification/generalization to your proposal.
I'd suggest supporting two files, with names like foo.rb.pre and
foo.rb.post.
Both files contain ordinary Ruby code, but ".pre" is run before foo.rb and
".post" is run after. .pre can be used to do what your .config file would
do. .post would be useful when foo.rb contains a buggy method and for
whatever reason I can't change foo.rb. Then I can redefine the problematic
method in .post.
Wayne

I'm not sure I fully understand the problem here, but wouldn't an easier
solution be to create a wrapper shell script that did nothing but :

ruby -rclrshim $1

and call it rubyclr or something? That would guarantee that the first file
required is always your clrshim (when its needed) and would in fact let you
call call other people's scripts, unmodified. Just my $.02

···

On 9/26/05, Wayne Vucenic <nightphotos@gmail.com> wrote:

On 9/26/05, John Lam <drjflam@gmail.com> wrote:

>
> Hi Wayne,
>
> >> I'm really glad to see that someone is working on a shim for CLR 2.0!
>
> Me too :slight_smile:
>
> >> Would doing something like this as the first 2 lines in the .rb file
> work?
> >> require 'clrshim'
> >> setSTA
>
> The problem is that require 'rubyshim' would have to be guaranteed to be
> the
> first line of code in a *program*. I'm not sure at all how I can make
that
> guarantee given the semantics of require.
>
> So hence my thinking around doing a platform-specific change to the Ruby
> runtime to check for a configuration file.
>
> -John
>
>

--
===Tanner Burson===
tanner.burson@gmail.com
http://tannerburson.com <---Might even work one day...

Hi Tanner,

ruby -rclrshim $1

I really like your *simple* solution to this problem. There are still some
corner cases (like what if someone doesn't call the wrapper?)

But it's more than good enough, and let's me refocus my attention on the
hard parts of building the shim.

Thanks for the suggestion.
-John