Support for Mono #3

Closed
mythz opened this Issue Mar 9, 2013 · 246 comments

Comments

Projects
None yet

mythz commented Mar 9, 2013

I understand this may not be a high priority, but many open source .NET frameworks and potential owin hosts want to ensure their frameworks and libraries work cross-platform.

I'm assuming there's going to be a bit of win specific bits, but it'd be nice if the parts that can be re-usable across platform can easily be decoupled and re-used from the bits that aren't.

Owner

tjanczuk commented Mar 10, 2013

I agree, it would be great for owin to have Mono support.

Owner

tjanczuk commented Mar 10, 2013

The basic async support from 4.5 appears to be there: http://www.mono-project.com/Compatibility. So all this needs it somebody's time to spike on it.

dragan commented Mar 10, 2013

I'm investigating it. Should be a good project for me to learn more about the internals of the Mono runtime. Plan on starting a pull request here soon so you can see how work progresses and answer questions.

Owner

tjanczuk commented Mar 10, 2013

Perfect, thank you. Looking forward to PR.


From: Dale Raganmailto:notifications@github.com
Sent: ý3/ý10/ý2013 11:02 AM
To: tjanczuk/owinmailto:owin@noreply.github.com
Cc: Tomasz Janczukmailto:tomasz@janczuk.org
Subject: Re: [owin] Support for Mono (#3)

I'm investigating it. Should be a good project for me to learn more about the internals of the Mono runtime. Plan on starting a pull request here soon so you can see how work progresses and answer questions.


Reply to this email directly or view it on GitHubhttps://github.com/tjanczuk/owin/issues/3#issuecomment-14685706.

Owner

tjanczuk commented Mar 12, 2013

Raising the bar, fresh from the csx branch:

welcome

dragan commented Mar 12, 2013

Very cool! It looks like adding mono support is possible and where we can contribute it back upstream without having to have a separate project. I'm going to work on it with another Mono contributor @joncham. Once we get some commits going, I'll start a pull request so you can see the progress and allow us to collaborate to make the changes upstream as painless as possible. The branch is located on my fork.

Owner

tjanczuk commented Mar 13, 2013

This is great, looking forward to it.

dragan commented Mar 15, 2013

FYI, I'm currently working on mono support ScriptCS and since you're going to be renaming the project, it will probably be best to start this after the rename has been done. /cc @joncham

Owner

tjanczuk commented Mar 15, 2013

I am looking forward to it. Renaming of the repo should not invalidate any forks. You will only need to update your upstream remotes on your local box.

dragan commented Mar 18, 2013

@tjanczuk Just an update, don't want you to think we disappeared.

@joncham and I collaborated this past weekend on the best way to tackle this issue. We put together a list of options that we want to run by you. Our goal is to make it easily maintainable between the platforms. Especially when developing on cross platforms, since I'm sure you have no interest in having a nix platform as well when doing releases.

Anyways, @joncham has the list and will be posting it soon.

Owner

tjanczuk commented Mar 18, 2013

This is exciting, looking forward to it!

Owner

tjanczuk commented Mar 20, 2013

An aspect of enabling Mono to consider is compilation of code at runtime. Supporting an extensible model for it that Mono could use is captured in #16.

Collaborator

joncham commented Mar 22, 2013

So, looking into adding mono support. It actually should not be too hard. However, at this point there would be minimal/no code sharing. Most of the C++ code is C++/CLI and can't be compiled/run with mono. Two options:

  1. We could just write mono support totally independent of existing code today. That would probably consist of two pieces. A C/C++ library that would be called by node and would interface with the mono embedding API, and also a C# assembly. The C/C++ library would keep the same API as the existing one used with .Net. The C# assembly would contain similar/identical logic to what is currently written in C++ for loading assemblies, calling methods, etc.
  2. Basically do the above, but try to share the assembly. This may just be an evolution after the first one occurs. Then the C/C++ layers would each do minimal processing and then call into the C# helper assembly.

For the C/C++ code that is going to be quite different for mono, do you prefer to have the code in the existing files today with #ifdef's or having seperate files with a prefix/suffix. edge.cpp and edge_mono.cpp for example.

Thanks,
Jonathan

Owner

tjanczuk commented Mar 22, 2013

Hello Jonathan, thank you for getting involved.

From the maintenance perspective it would be preferable to do something closer to your option 2. The V8 code and the pure CLR code could be reused between the targets, and the differences between Mono and .NET scoped to runtime hosting and p\invoke mechanisms. Now that would require current implementation to be refactored from using C++\CLI to using raw CLR hosting APIs.

And this is where I am having second thoughts about that approach. Looking at the CLR hosting APIs I can see the countless sleepless nights spent refactoring the code that end in a divorce. Given that edge.js is about 1 KLOC of C++\CLI code right now, and that I don't foresee it growing much more, it may be more appealing to just re-write it all for mono. Perhaps we can try to do some targeted reuse attempts at the source code level for code that is pure C++ or pure CLR.

What do you think? I will also consult with someone versed in CLR hosting to get an idea what I would be getting myself into by going away from C++\CLI.

Owner

tjanczuk commented Mar 23, 2013

I've talked to a local CLR expert. Refactoring the C++\CLI code to use CLR hosting APIs would bring in several hundred lines of code out of the gate along with added complexity. Given that I think we will be better off cloning the 1-2KLOC of source code of edge.js for Mono and opportunistically reusing individual files, which is closer to option 1 above.

js-n commented Apr 3, 2013

Unfortunately I don't really have the C/C++ skills to help on the hosting side, but I'm really looking forward to this and would be happy to help with testing and C#/JS.

Collaborator

joncham commented Apr 16, 2013

Just a status update. I haven't pushed anything yet, but locally I have things building with mono on Windows and am working through the test set one by one. edge.func tests are passing ATM.

Due to the fact that the code is largely a complete rewrite with similar structure, I'll ask for input once I push to my repo. Not sure if we should just mirror all the .h/.cpp files under a mono directory (what I am doing currently) or if there is a better approach.

Owner

tjanczuk commented Apr 16, 2013

Sounds great, looking forward to providing feedback.

I think we can start with a complete clone of code in mono\edge.h and mono\edge.cpp, we can refactor for reuse from there if possible.

How does building work? Do you have it building with node-gyp?

What are the runtime and development time prerequisites on a *nix platform?

7sharp9 commented Apr 22, 2013

Maybe something in https://github.com/fogzot/vroomjs is of interest, cross platform no managed C++ bits.

Collaborator

joncham commented Apr 30, 2013

Here is start: https://github.com/joncham/edge/tree/mono-support

I need to add build instructions. This is Windows only ATM. You need to install a recent mono, and then build the runtime yourself since mono doesn't ship with a linkable import library.

I am attempting to use gyp.

Pre-reqs at dev time will be a mono installation with include/lib/bin dirs. I spent a while trying to figure out how to configure the gyp build on Windows so I can pass a mono install but I am still a bit lost. At runtime you'll need a loadable (lib)mono.dll/.so and a working compiler for the edge-cs support.

At this point the edge.func tests pass locally for me. I am still cleaning things up, and then I need to add more support to ClrFunc::MarshalV8ToCLR and ClrFunc::MarshalCLRToV8 for complex types to not crash on subsequent tests.

7sharp9 commented Apr 30, 2013

I look forward to trying this when it will work on osx. :-)

On 30 Apr 2013, at 04:55, Jonathan Chambers notifications@github.com wrote:

Here is start: https://github.com/joncham/edge/tree/mono-support

I need to add build instructions. This is Windows only ATM. You need to install a recent mono, and then build the runtime yourself since mono doesn't ship with a linkable import library.

I am attempting to use gyp.

Pre-reqs at dev time will be a mono installation with include/lib/bin dirs. I spent a while trying to figure out how to configure the gyp build on Windows so I can pass a mono install but I am still a bit lost. At runtime you'll need a loadable (lib)mono.dll/.so and a working compiler for the edge-cs support.

At this point the edge.func tests pass locally for me. I am still cleaning things up, and then I need to add more support to ClrFunc::MarshalV8ToCLR and ClrFunc::MarshalCLRToV8 for complex types to not crash on subsequent tests.


Reply to this email directly or view it on GitHub.

Owner

tjanczuk commented Apr 30, 2013

Jonathan,

it is great to see this, thank you. A few questions/comments/suggestions:

  1. Could you open a pull request back to my repo? This way we will be able to comment on the source diff.
  2. High level, I think Mono support should primarily target Mac and *nix, and Windows version should use regular .NET. Edge already runs on Windows, so the key benefit is enabling it on Mac and *nix. Are we on the same page? Did you have chance to try this on Mac or *nix?
  3. I think it is more important to have smooth runtime experience than development time experience (by development time I mean folks who need to change edge.js code). What do you think is the most reasonable runtime experience? What are the dependencies that must be installed and what is their size? Is it reasonable to include them in the repo? Is this something that can be downloaded from some public share during npm install edge? Or is this something people have to download/install/build on their own prior to installing edge?
  4. Were any changes required in edge-cs, or does it work as-is?
  5. I notice there are at least some files that are no different between C++\CLI and Mono (e.g. V8SynchronizationContext). Did you have a chance to consider best approach to source code reuse between .NET and Mono versions?
  6. High level, what is the remaining work required and do you need help with any of it?

Thanks!

Collaborator

joncham commented Apr 30, 2013

@xpaulbettsx I am compiling mono runtime on Windows to a) easily produce a link library for the DLL. b) to debug into runtime as needed when things crash.

Owner

tjanczuk commented Apr 30, 2013

@xpaulbettsx sorry I think I accidentally deleted your comment.

If we assume edge continues to use .NET on Windows and Mono usage targets Mac and *nix, then the export library is non-issue, a temporary artifact of Windows development. Jonathan, is there a particular reason you have chosen Windows as your development environment? It would be good to switch to *nix/MacOS soon. What would be a reasonable checkpoint to do it?

Collaborator

joncham commented Apr 30, 2013

I chose Windows as I am more comfortable debugging C/C++ (edge and mono runtime) in VS. I am working on getting things compiling on OSX right now. I agree main platforms to target are OSX/Linux, but I'll keep Windows going from at least a dev point for my debugging sanity.

Collaborator

joncham commented Apr 30, 2013

Responses:

  1. Pull request opened: #48
  2. Agree. I just prefer to debug on Windows. I will work on OSX/Linux as well.
  3. Not sure yet. I think we may just be able to require users to have installed mono from http://www.go-mono.com/mono-downloads/ and configured properly (i.e. in their PATH/LD_LIBRARY_PATH/DYLD_LIBRARY_PATH or via other loader mechanisms)
  4. It seemed to work as is. But that was only on Windows
  5. I have not. I figure get it working first and then try to cleanup and reuse code. V8SyncronizationContext will definitely need changed for non-Windows platform though.
  6. I can keep working on the marshalling V8 <-> CLR. I will take any help obviously, but especially implementing V8SynchronizationContext for non-Windows would be helpful. Also, cleaning up gyp build would be great too.
Owner

tjanczuk commented Apr 30, 2013

I can help with V8SynchornizationContext when the time comes. It should be a matter of switching to native synchronization primitives. I will see if libuv can help here, maybe I can proactively make this class native and cross-platform.

Contributor

paulcbetts commented Apr 30, 2013

@tjanczuk If you're on Mono you've got Glib, check out https://developer.gnome.org/glib/2.30/glib-Threads.html

Owner

tjanczuk commented Apr 30, 2013

Where possible I would like to avoid platform-dependent #if/#else in code. That is why I am looking at http://nikhilm.github.io/uvbook/threads.html#synchronization-primitives as a potential source of platform-agnostic synchronization primitives.

Owner

tjanczuk commented May 1, 2013

Am I correct to assume I need Mono 3.x to compile async/await code, or is Mono 2.x sufficient? What is the minimum version? Is the fact Mono 3.x is only Beta concerning?

7sharp9 commented May 1, 2013

The minimum version of Mono for async await is 2.11 I think. The Mono 3.x branch is fairly stable. I think there will be an interim period waiting for people to move over to 3.x before they tag it as stable and depreciate support on the 2.x versions.

Owner

tjanczuk commented May 1, 2013

I see, thanks. Where do I get 2.11 from? The latest stable at http://www.go-mono.com/mono-downloads/download.html seems to be 2.10, then it goes to 3.x?

7sharp9 commented May 1, 2013

I have seen it in their update channel somewhere but to be honest I would just use 3.0.10. I think most people will be using the 3.x series. We don't even support the 2.x series in the F# binding for MonoDevelop anymore.

Collaborator

joncham commented May 1, 2013

I'll be building/testing against 3.x series. The embedding API should not have changed between late 2.x and 3.x, so it well probably work on 2.x mono installs as well. The biggest issue would be compiler and class library support for the latest .Net features.

7sharp9 commented May 1, 2013

How do I go about installing and testing this on Osx? I would like to try this out with my edge-fs compiler.

Dave.

On 1 May 2013, at 15:17, Jonathan Chambers notifications@github.com wrote:

I'll be building/testing against 3.x series. The embedding API should not have changed between late 2.x and 3.x, so it well probably work on 2.x mono installs as well. The biggest issue would be compiler and class library support for the latest .Net features.


Reply to this email directly or view it on GitHub.

Owner

tjanczuk commented May 1, 2013

I am new to Mono, @joncham any hints as to how to get started? I would expect the changes to include:

  1. Modifying binding.gyp
  2. Building native edge.node libraries for Darwin/other *nix platforms
  3. Using EDGE_NATIVE environment variable to point to the native image while ad-hoc testing during development
  4. Checking in edge.node for supported platforms in new subdirectories following the pattern under lib\native.

7sharp9 commented May 1, 2013

Ah ok, I thought the pending pull request would allow some functionality to work. As soon as it works then I can test it anyway.

Contributor

paulcbetts commented May 1, 2013

@tjanczuk I don't think you need these steps on Mono, node-gyp does that all for you - no custom rigging necessary.

dragan commented May 1, 2013

Yeah, still work to do to have it work under Mono on *nix platforms, that's where I come in. @joncham Started the work with getting the files in place, since I'm still new to the native mono runtime code. I was just waiting on his pull request, so I can jump in. If we're not fast enough, anybody is welcome to tackle this. Just let me know so we're not duplicating efforts.

Collaborator

joncham commented May 1, 2013

I will attempt to get OSX to compile after work today. ATM with my branch you can

  1. Install Mono MDK for OSX: http://download.mono-project.com/archive/3.0.10/macos-10-x86/MonoFramework-MDK-3.0.10.macos10.xamarin.x86.dmg
  2. Setup node properly and checkout edge
  3. Configure edge: node-gyp configure -RUNTIME=mono
  4. Build: node-gyp build -debug
  5. This should produce compiler errors for v8synchronizationcontext.cpp that need fixed.
  6. After that I am guessing native part will build, and then gyp file may need tweaked to build managed part correctly.
Contributor

paulcbetts commented May 1, 2013

The trickiest part on OS X is that the default Mono distribution is 32-bit only, whereas Homebrew really wants to build 64-bit Node. You either have to build 64-bit Mono or 32-bit Node to fix this. Linux of course doesn't have this problem :)

Collaborator

joncham commented May 1, 2013

Note I build a managed helper assembly (monoembedding.cs). In general when embedding mono, it's best to do as much in managed code as possible and just use the C embedding API when needed. The embedding API is verbose at times and doesn't have access to things like generics.

Collaborator

joncham commented May 1, 2013

I am using a 32-bit node binary from: http://nodejs.org/download/

Owner

tjanczuk commented May 1, 2013

@dragan Perfect, thanks a lot!

@xpaulbettsx @joncham @dragan Do you think it is feasible to build edge.node for Mono on the fly at npm install edge time? Will folks be required to install any other dependencies compared to the minimum required at runtime? Is it worthwhile to consider pre-building the binaries just like on Windows?

Contributor

paulcbetts commented May 1, 2013

Do you think it is feasible to build edge.node for Mono on the fly at npm install edge time?

Yes, this is how npm works by default on !Windows. The dream of the 90s Linux user is alive in node.js

Will folks be required to install any other dependencies compared to the minimum required at runtime?

They should just have to have a C compiler and Mono installed, quite reasonable

Is it worthwhile to consider pre-building the binaries just like on Windows?

This is Weird™ and will most likely not work because of the 10000000 slightly different Linux distros and OS X versions

Collaborator

joncham commented May 1, 2013

I'd be most useful continuing to work on embedding and marshaling V8 <->CLR types. If someone wants to handle v8synchronizationcontext.cpp compilation issues on OSX and any additional build stuff that would be great. If not I'll pick that up.

Owner

tjanczuk commented May 1, 2013

@xpaulbettsx Building on the fly would be ideal if we can pull it off without imposing extra end user grief.

The trickiest part on OS X is that the default Mono distribution is 32-bit only

So how would you go about enabling 64 bit without requiring unorthodox Mono installations from end users of edge.js?

Owner

tjanczuk commented May 1, 2013

@joncham I can take a stab at v8synchronizationcontext. I will try to make it vanilla native C++ and cross platform in the main branch, you can then merge from there.

Collaborator

joncham commented May 1, 2013

@tjanczuk great. Ideally, I'll get mono based edge passing all tests on Windows (parity with .Net). Then we have baseline for mono on other platforms.

Contributor

paulcbetts commented May 1, 2013

So how would you go about enabling 64 bit without requiring unorthodox Mono installations from end users of edge.js?

The easiest way would to try to push a new Homebrew package, "mono-64bit" or something - that way the instructions would be:

  1. brew install mono-64bit
  2. npm install edge-cs
Owner

tjanczuk commented May 1, 2013

I know this would be an unconventional use of NPM, but would it make sense to ship this package on NPM rather than Homebrew? One less dependency for end users, and they already obviously have NPM...

Collaborator

glennblock commented May 1, 2013

The down side of building on the fly on Windows is you need to install Python in order to use node-gyp.

It would be great if it can build, but also can have the binary for those that don't want to build, or for hosting in environments like Azure websites where gyp builds are not supported.

-----Original Message-----
From: "Tomasz Janczuk" notifications@github.com
Sent: ‎5/‎1/‎2013 10:49 AM
To: "tjanczuk/edge" edge@noreply.github.com
Subject: Re: [edge] Support for Mono (#3)

@xpaulbettsx Building on the fly would be ideal if we can pull it off without imposing extra end user grief.
The trickiest part on OS X is that the default Mono distribution is 32-bit only
So how would you go about enabling 64 bit without requiring unorthodox Mono installations from end users of edge.js?

Reply to this email directly or view it on GitHub.

Collaborator

joncham commented May 1, 2013

I know nothing of Homebrew or NPM, but you need a whole mono installation for mono support. i.e. the runtime binary, class library assemblies, compilers, etc.

Collaborator

glennblock commented May 1, 2013

@tjanczuk installing brew is not a blocker. Many mac users use it or mac ports. I wouldn't jump through too many hoops to try to make that happen.

Owner

tjanczuk commented May 1, 2013

@glennblock I think we will continue shipping binaries for Windows, there is just too much pain building on the fly. We are only discussing the best course of action for Mono (i.e. OSx/*nix).

@glennblock @joncham I just find it bizzare we would want to involve two package managers instead of one given the choice, at least from the prerequisites perspective. Is there some additional advantage of Homebrew in this case?

Contributor

paulcbetts commented May 1, 2013

I just find it bizzare we would want to involve two package managers instead of one given the choice, at least from the prerequisites perspective. Is there some additional advantage of Homebrew in this case?

99.9% of people doing node.js on Mac already have homebrew installed, it's just a shortcut. You can also say "Build Mono in this directory yourself", but it'd be far easier to just tell them to brew install something

Owner

tjanczuk commented May 1, 2013

OK sounds good then. What is the process of publishing something to Homebrew?

dragan commented May 1, 2013

I'm familiar with Homebrew and it's probably the best way to install the Mono dependency until the mono packages that are released by the Mono project support 64-bit. That or use my tool within the npm install to compile and install mono for *nix platforms.

The only drawback of including mono in our package is the fact that compiling mono takes a while depending on the hardware. It's probably best just to have the individual install it first.

@tjanczuk You just need to create a formula, which is just ruby code.

Owner

tjanczuk commented May 1, 2013

Where do the binaries physically live in case of Homebrew? Does it provide some storage beyond the Ruby scripts (which I assume is on GitHub)?

Contributor

paulcbetts commented May 1, 2013

@tjanczuk They live in /usr/local, as if you installed it by hand via ./configure --prefix=/usr/local

Collaborator

glennblock commented May 1, 2013

On the machine? It installs into a central cellar folder.

-----Original Message-----
From: "Tomasz Janczuk" notifications@github.com
Sent: ‎5/‎1/‎2013 11:10 AM
To: "tjanczuk/edge" edge@noreply.github.com
Cc: "Glenn Block" glenn.block@gmail.com
Subject: Re: [edge] Support for Mono (#3)

Where do the binaries physically live in case of Homebrew? Does it provide some storage beyond the Ruby scripts (which I assume is on GitHub)?

Reply to this email directly or view it on GitHub.

Owner

tjanczuk commented May 1, 2013

I meant where are the binaries downloaded from when I say brew install mono-64bit?

Contributor

paulcbetts commented May 1, 2013

@tjanczuk The sources are downloaded from the official locations (wherever that may be) or cloned from a Git repo, then built locally

Owner

tjanczuk commented May 1, 2013

Oh I see, so homebrew would also build Mono locally? I thought it would pull in pre-built binaries, a la apt-get. Well in that case why don't we just make Mono a submodule of edge.js just like node does with its dependencies (e.g. V8 or libuv)? The sources are on GitHub anyway, right?

Contributor

paulcbetts commented May 1, 2013

@tjanczuk That means that every project you use edge with would carry around a ~200MB install that had to be built from source

Owner

tjanczuk commented May 1, 2013

That does not sound pragmatic, yes. Let me make sure I understand then the proposal:

  1. Mono is installed manually by the user as a prerequisite using Homebrew. Now does this install binaries, or does it install sources that are then locally compiled?
  2. npm install edge will install sources and build them against the local Mono installation.

7sharp9 commented May 1, 2013

Don't forget Mono can be updated via MonoDevelop or Xamarin Studio IDE's automatically.

Contributor

paulcbetts commented May 1, 2013

Now does this install binaries, or does it install sources that are then locally compiled?

It downloads sources and compiles them

npm install edge will install sources and build them against the local Mono installation.

Nailed it.

Don't forget Mono can be updated via MonoDevelop or Xamarin Studio IDE's automatically.

Well, so actually at the moment you have to hack PKG_CONFIG_PATH by hand in order to find the system Mono, it's actually an advantage that you install a separate Mono via homebrew, because pkg-config will now return the homebrew'd Mono.

dragan commented May 1, 2013

Well, so actually at the moment you have to hack PKG_CONFIG_PATH by hand in order to find the system
Mono, it's actually an advantage that you install a separate Mono via homebrew, because pkg-config will now
return the homebrew'd Mono.

Yes, the user will need to install a parallel version of Mono. On OSX, it is easy with just using Homebrew to compile sources that we specify in the formula. We will then need to set environment variables when compiling Edge against that version of Mono. However, on Linux, we will need to think of something else as the packages installed via apt-get for example can be out of date depending on the distro. It may require a little more of a manual process to get up and running. Hopefully, mono-build can help with this process. It's similar to Homebrew, but specifically for mono. Using it in conjunction with mope should ease the process maybe, if the user doesn't want to do all the steps themselves?

7sharp9 commented May 1, 2013

A quick install is always a winner, if you have to do too much manual stuff then people wont even bother. A clear how to guide can mitigate some of that though.

Collaborator

glennblock commented May 1, 2013

+1

And Paul, that is a fabulous point.

On Wed, May 1, 2013 at 12:46 PM, Dave Thomas notifications@github.comwrote:

A quick install is always a winner, if you have to do too much manual
stuff then people wont even bother. A clear how to guide can mitigate some
of that though.


Reply to this email directly or view it on GitHubhttps://github.com/tjanczuk/edge/issues/3#issuecomment-17302064
.

Owner

tjanczuk commented May 1, 2013

That was my original intent behind publishing binaries. It makes for a much more seamless installation experience. Installing edge.js on Windows is a breeze today because we don't compile anything. Would it make sense to reconsider this approach?

As far as compiling from sources goes if this is what we end up doing, perhaps we could consider NPM after all, since that would have the benefit of working the same way on MacOS and *nix? I imagine this could look like this:

  1. User calls npm install mono-64bit
  2. As part of the install script for the package the following happens:
  3. Mono sources are downloaded from GitHub (perhaps using the tarball endpoint to avoid dependency on Git)
  4. Sources are compiled locally and installed wherever we need them.
Contributor

paulcbetts commented May 1, 2013

Here's a Homebrew script to install Mono 64-bit:

require 'formula'

class Mono < Formula
  url 'http://download.mono-project.com/sources/mono/mono-3.0.7.tar.bz2'
  homepage 'http://www.mono-project.com'
  sha1 '0699c119f6aded3912797b45049932609020bc29'

  def install
    args = ["--prefix=#{prefix}",
            "--with-glib=embedded",
            "--enable-nls=no"]

    args << "--host=x86_64-apple-darwin10" if MacOS.prefer_64_bit?

    system "./configure", *args
    system "make"
    system "make install"
  end
end

Note that 3.0.10 broke --enable-nls=no so I had to use 3.0.7. I'm sure it's fine :) If you want to try this out, copy-paste that code into /usr/local/Library/Formula/mono.rb

Any reason to recommend Mono64 at this point? It is the least tested port right now.

Contributor

paulcbetts commented May 1, 2013

@migueldeicaza The main reason is that Node itself is built by default as 64-bit, so people will be trying to load 32-bit Mono into a 64-bit Node process. Asking people to install 32-bit Node is also possible, but less straightforward (since Homebrew doesn't really grok having > 1 of the same package installed)

Collaborator

joncham commented May 1, 2013

I am not a homebrew user on OSX ATM. At this point a node 32-bit binary package and a mono 32-bit MDK install (from the respective websites) and things are good to go. Homebrew seems to be causing the 32/64-bit issue and also the changes in this pull request: joncham#2 assume pkg-config existance on OSX. Which I guess I need from Homebrew? Could we provide a Homebrew and non-Homebrew setup?

Contributor

paulcbetts commented May 1, 2013

@joncham Ugh, so gross. How can we make this sane...

Collaborator

joncham commented May 1, 2013

I'm open to whatever. I figured I would need to hit HomeBrew at some point with Apple removing stuff from the Command Line Tools, just haven't yet. For me the node binary and mono MDK 'just work', but if 95% of devs out there are going to be using HomeBrew (and a 64-bit node) then so be it.

Owner

tjanczuk commented May 1, 2013

So what is the installation experience going to look like on *nix if Homebrew is used on MacOS?

Contributor

paulcbetts commented May 1, 2013

So what is the installation experience going to look like on *nix if Homebrew is used on MacOS?

brew install mono   ## Wait forever, but only 1x per machine
npm install edge
Owner

tjanczuk commented May 1, 2013

So Homebrew is also common on *nix? How do I get it on say Ubuntu?

Owner

tjanczuk commented May 1, 2013

Also, does the end user need to be explicit about installing 64 or 32 bit mono to match his node installation, or can we streamline this somehow? Going by machine arch is not enough as you can run 32 bit node on 64 machine.

Owner

tjanczuk commented May 1, 2013

brew install mono ## Wait forever, but only 1x per machine

How long roughly is "forever"?

js-n commented May 1, 2013

Homebrew is OSX specific. Installing mono takes about 5 minutes to compile on my machine (2012 MacBook Pro). It automatically takes care of 64/32 bit stuff when it configures the build for you.

Contributor

paulcbetts commented May 1, 2013

How long roughly is "forever"?

Like 5-10 minutes or so.

Homebrew is OSX specific

That's the advantage of this approach: being able to use pkg-config means that on Linux it Does The Right Thing™ automatically

Owner

tjanczuk commented May 1, 2013

I must be missing something. You previously said (#3 (comment)) you would use brew install mono on *nix as well to get Mono installed. @jden says Homebrew is OSX specific. So how do we get Mono bits installed on *nix?

Contributor

paulcbetts commented May 2, 2013

@tjanczuk Ah sorry, I totally misread your question - on *nix we can just use the existing packages, everything works:

apt-get install mono
npm install edge
Owner

tjanczuk commented May 2, 2013

Great, that is what I expected. So on *nix we already have both 32 and 64 binary packages available through apt-get?

Brew is a MacOS specific technology and it is a popular way of getting software installed.

Linux has too many different ways of installing packages, and even across distributions using the same packaging system, the package name is different. So for Linux the answer is: build for your own distribution, and wait for the various distributions to properly package your work to match the requirements of their specific environment.

On Debian/Ubuntu they use apt-get, on Red Hat they use "yum", on OpenSUSE "zypper", on Arch "pacman". So do not try to address how to install Mono, just make it a requirement that your user needs to satisfy.

That said, in both cases (Linux and MacOS) you can use pkg-config tool to find the compiler flags and linker flags needed to link the pieces together.

Contributor

paulcbetts commented May 2, 2013

In other words, Linux people are used to their lives being terrible, so we don't have to worry about trying to optimize 32 vs 64-bitness for them :)

Owner

tjanczuk commented May 2, 2013

@migueldeicaza Thanks for the explanation. In practical terms, do you know what the experience is of installing Mono on the popular *nix platforms? In particular, you mentioned 64 bit is non-standard. This would be slightly concerning as I think majority of node.js use today is 64 bit.

woloski commented May 2, 2013

+1

Focus on a good dev experience on mac and win. Also mono is not a lightweight dependency so it is ok to install w/homebrew.

Looking fwd to make use of edge + mono on mac and ubuntu! As soon as there is something will try it

On Wednesday, 1 de May de 2013 at 23:48, Paul Betts wrote:

In other words, Linux people are used to their lives being terrible, so we don't have to worry about trying to optimize 32 vs 64-bitness for them :)


Reply to this email directly or view it on GitHub (#3 (comment)).

64-bit is the standard on Linux.

It is just not the default on OSX due to so much software being written with Mono that consumed 32 bit APIs (Gtk and MonoMac being the major issues), and that is why you have to resort to homebrew, because the official Mono distribution only distributes 32 bit binaries.

Owner

tjanczuk commented May 2, 2013

Folks, how important do you think it is to support edge.js with node.js 0.6.x and older on MacOS and *nix? The issue is that the synchronization primitives required to port V8SynchronizationContext were only added to the version of libuv that ships with 0.8.x.

I know I want to support 0.6.x on Windows, so I will already have some platform specific code in there, but I was hoping to use libuv to abstract the MacOS/*nix differences.

mythz commented May 2, 2013

I'm ok with OSX/*nix to require a later version for a young/bleeding_edge project like edgejs. All projects will be greenfield so I don't see any pressing need to support legacy software versions.

Owner

tjanczuk commented Mar 26, 2014

Folks, as an aside, I notice the source code file the brew formula downloads to later compile mono 64bit is 81MB in size. I compare it to the 130MB download of the *.pkg file with x86 installer and (assuming 64bit package would be similar in size) I begin wonder if we should just pre-compile the 64bit build, ask people to download the extra 50MB. That would save them 15 mins compiling. If we were to pre-compile mono 64 bit, is it going to be portable across the OSX versions?

Owner

tjanczuk commented Mar 26, 2014

@bmavity @joncham I took the @bmavity repo and moved it into the mono branch of edge. I brought back the binding.gyp target that builds MonoEmbedding.exe based on @joncham fork. I modified the code and binding.gyp so that MonoEmbedding.exe is binplaced next to edge.node during build, and edge.node picks it up from there. This solves the problem of where to get MonoEmbedding.exe from. I also added basic building instructions for OSX+Mono to readme.md.

When I run mocha I get a bunch of failures related to the absence of Edge.Tests.dll. This assembly had been built by tools\test.bat on Windows, invoked through npm test script configured in package.json. The first order of business would be to replace test.bat with test.js that will compile Edge.Tests.dll in a platform specific way - similarly to what we do for installation in tools\install.js.

Going forward I suggest we make small incremental changes that we code review via pull requests to the edge/mono branch.

Let me know what you think. I really appreciate your participation in this effort.

Owner

tjanczuk commented Mar 26, 2014

Regarding Edge.Tests.dll, we should probably just make it compile as part of binding.gyp.

Owner

tjanczuk commented Mar 26, 2014

@bmavity @joncham I just added you as collaborators to the repo so that we can work on the mono branch. Let's make sure at least one other person reviews a pull request before it is merged (into the mono branch).

Owner

tjanczuk commented Mar 28, 2014

I investigated a bit, made some fixes, and we are now able to run C# code from both pre-compiled assemblies as well as compiled on the fly from C# source code embedded in Node.js. Here is the gist:

  1. Tests were attempting to load the precompiled test DLL from a location that was not specified in a cross-platform way. This was causing a number of tests that load the pre-compiled test DLL to fail. I fixed this with 1fa026f
  2. The edge-cs dependency was loading the pre-compiled C# compiler provider from a location that was not specified in a cross-platform way. This was causing all tests that specify C# literals to be compiled by edge on the fly to fail. I fixed this in edge-cs which I released on NPM as edge-cs@0.2.5, and subsequently took a dependency on that version of edge-cs in edge itself with 9c827df

When you sync to the latest changes, you will need to re-run npm install to pick up the new edge-cs@0.2.5 dependency.

The next issue that needs to be addressed is marshalling of data between Node.js and CLR (and likely from CLR to Node.js). Specifically, a number of failures I am seeing is related to marshaling of JavaScript objects as IDictionary<string,object> to CLR in Mono as opposed to ExpandoObjects as it is the case in .NET on Windows. I believe this is because the Mono fork was created before the dynamics support was added. @joncham any chance you can lend your hand or expertise here? I also notice a number of marshaling code paths is commented out, so I expected many more issues in this space remain to be addressed.

@bmavity I am going not to take your pull request #111 since I already addressed some of the issues with latest checkins.

Collaborator

glennblock commented Mar 28, 2014

You my friend are awesome!

A picture tells 1000 words, or 1000 op codes :-) So here's one for you :-)

[image: Inline image 1]

On Fri, Mar 28, 2014 at 2:38 AM, Tomasz Janczuk notifications@github.comwrote:

I investigated a bit, made some fixes, and we are now able to run C# code
from both pre-compiled assemblies as well as compiled on the fly from C#
source code embedded in Node.js. Here is the gist:

  1. Tests were attempting to load the precompiled test DLL from a
    location that was not specified in a cross-platform way. This was causing a
    number of tests that load the pre-compiled test DLL to fail. I fixed this
    with 1fa026fhttps://github.com/tjanczuk/edge/commit/1fa026ff12ec1b5a8c58056372e542d7e309264c
  2. The edge-cs dependency was loading the pre-compiled C# compiler
    provider from a location that was not specified in a cross-platform way.
    This was causing all tests that specify C# literals to be compiled by edge
    on the fly to fail. I fixed this in edge-cshttps://github.com/tjanczuk/edge-cs/commit/c6bfd366362cf47715def5debf908931e27f2f04which I released on NPM as edge-cs@0.2.5,
    and subsequently took a dependency on that version of edge-cs in edge
    itself with 9c827dfhttps://github.com/tjanczuk/edge/commit/9c827df6d52e1b5d468b5fb15f7a805929ddf261

When you sync to the latest changes, you will need to re-run npm installto pick up the new
edge-cs@0.2.5 dependency.

The next issue that needs to be addressed is marshalling of data between
Node.js and CLR (and likely from CLR to Node.js). Specifically, a number of
failures I am seeing is related to marshaling of JavaScript objects as
IDictionary to CLR in Monohttp://9c827df6d52e1b5d468b5fb15f7a805929ddf261as opposed to ExpandoObjects
as it is the case in .NEThttp://9c827df6d52e1b5d468b5fb15f7a805929ddf261on Windows. I believe this is because the Mono fork was created before the
dynamics support was added. @joncham https://github.com/joncham any
chance you can lend your hand or expertise here? I also notice a number of
marshaling code paths is commented out, so I expected many more issues in
this space remain to be addressed.

@bmavity https://github.com/bmavity I am going not to take your pull
request #111 #111 since I already
addressed some of the issues with latest checkins.

Reply to this email directly or view it on GitHubhttps://github.com/tjanczuk/edge/issues/3#issuecomment-38894935
.

Collaborator

bmavity commented Mar 28, 2014

@tjanczuk I will look at the marshalling over the weekend. But I can't promise any progress. Hopefully @joncham can shave some time off of my research. ;)

Collaborator

joncham commented Mar 28, 2014

My ears are burning. Sure, I'll lend a hand. I'd like my local setup to match what you are running. Are these the latest directions? https://github.com/tjanczuk/edge/tree/mono#building-on-osx

Owner

tjanczuk commented Mar 28, 2014

Yes. We also need to figure out the npm install hang issue. @glennblock also hit it last night. It does not happen on rerun for me. I wonder if this is related to how node-gyp is installed.


From: Jonathan Chambersmailto:notifications@github.com
Sent: ý3/ý28/ý2014 8:29 AM
To: tjanczuk/edgemailto:edge@noreply.github.com
Cc: Tomasz Janczukmailto:tomasz@janczuk.org
Subject: Re: [edge] Support for Mono (#3)

My ears are burning. Sure, I'll lend a hand. I'd like my local setup to match what you are running. Are these the latest directions? https://github.com/tjanczuk/edge/tree/mono#building-on-osx


Reply to this email directly or view it on GitHubhttps://github.com/tjanczuk/edge/issues/3#issuecomment-38931836.

Owner

tjanczuk commented Mar 30, 2014

I fixed marshalling of V8 Object to Mono ExpandoObject and V8 Date to Mono DateTime with 199de07. You can now run the following code:

var edge = require('../edge/lib/edge')
    , assert = require('assert')

var edgeTestDll = '../edge/test/Edge.Tests.dll';

var func = edge.func({
    assemblyFile: edgeTestDll,
    methodName: 'MarshalIn'
});

var payload = {
    a: 1,
    b: 3.1415,
    c: 'foo',
    d: true,
    e: false,
    f: new Buffer(10),
    g: [ 1, 'foo' ],
    h: { a: 'foo', b: 12 },
    i: function (payload, callback) { },
    j: new Date(Date.UTC(2013, 07, 30))
};
func(payload, function (error, result) {
    console.log('RESULT', error, result);
    assert.ifError(error);
    assert.equal(result, 'yes');
});

The next issue in line is fixing marshalling of symmetrical Mono object back to JS. Basically making the successfuly marshals data from .net to node.js test case from 102_node2net.js work.

@joncham I notice the documentation for Mono embedding is pretty much nonexistent. Is that a fact of is Google blacklisting Mono?

Collaborator

joncham commented Mar 30, 2014

@tjanczuk I made one comment on your changes related to unnecessary boxing.
199de07#commitcomment-5845397

There is not a lot of documentation on embedding mono:
http://www.mono-project.com/Embedding_Mono

The very old examples here:
https://github.com/mono/mono/tree/master/samples/embed

Owner

tjanczuk commented Mar 30, 2014

@joncham thanks for the hint and links, I undid the boxing with 3ab6a6f

I noticed a pattern in the code you wrote where you tend to create managed instances in managed code you invoke through reflection. What is the reason for that? Is creating object instances through Mono embedding APIs not efficient or clumsy compared to calling to managed code? What is the cost of calling to managed code for such purpose?

Any chance you can help fix some of the remaining V8<->Mono marshaling issues?

Owner

tjanczuk commented Apr 2, 2014

@joncham do you know if there a civilized way of iterating over IDictionary<string,object> using Mono embedding APIs? The lack of documentation and Google support is really annoying.

Owner

tjanczuk commented Apr 4, 2014

I've done a lot more Mono->V8 marshalling work with 6ec6844. At this point we have parity with CLR->V8 in terms of data type support and many more tests are passing. This means you can pass objects with arbitrary members from C# to JavaScript.

The next order of business appears to be fixing up the exception handling during marshalling (currently exceptions or ignored), and then plowing through the remaining test failures one by one.

Collaborator

bmavity commented Apr 4, 2014

This is great news. It's the first time I've been able to run through all the tests without some type of mono crash. I noticed all the async tests are failing. On the surface it seemed like something that would be straightforward to fix. I looked through the code try to tackle it. Any hints on that or the exception handling? I can try to look at it, although it may be less work for you to do it yourself than it would be to help me. :)

Owner

tjanczuk commented Apr 5, 2014

@bmavity the exception handling should not be that scary. Basically we need to achieve parity with the currently commented out section https://github.com/tjanczuk/edge/blob/mono/src/mono/clrfunc.cpp#L250-L258. To do this, we need to review all downstream function calls made from ClrFunc::MarshalCLRToV8 looking for places that can result in an exception. These mostly boil down to calls to mono_runtime_invoke which return exceptions, if thrown, as out params. Most calls to mono_runtime_invoke currently ignore these out params, like here: https://github.com/tjanczuk/edge/blob/mono/src/mono/monoembed.cpp#L114. The goal is to bubble the exceptions up to ClrFunc::MarshalCLRToV8 and convert them to a V8 exception like it was done before: https://github.com/tjanczuk/edge/blob/mono/src/mono/clrfunc.cpp#L253. One place where this is actually done right is at https://github.com/tjanczuk/edge/blob/mono/src/mono/clrfunc.cpp#L253.

Can you take a stab at this? In the meantime I will be looking at other failures when CLR calls back to JS using an exported function.

Collaborator

bmavity commented Apr 5, 2014

I'll take a look at it tomorrow morning. Thanks for the pointers. :)

Collaborator

glennblock commented Apr 5, 2014

Thanks all for the progress. I have another edge talk linked up in Phoenix
on wed. I will be showing off your efforts :-)

On Saturday, April 5, 2014, Brian Mavity notifications@github.com wrote:

I'll take a look at it tomorrow morning. Thanks for the pointers. :)

Reply to this email directly or view it on GitHubhttps://github.com/tjanczuk/edge/issues/3#issuecomment-39648269
.

Collaborator

bmavity commented Apr 6, 2014

@tjanczuk Ok, for clarification, you don't want the giant try/catch block in the ClrFunc::MarshalCLRToV8 to be used, because uncommenting it causes all the sync exception tests to pass. Also, the link you posted for the place where it is done right points to the same commented code. Was that intentional?

Owner

tjanczuk commented Apr 6, 2014

@bmavity The giant try/catch block in ClrFunc::MarshalCLRToV8 cannot be used - it is a C++/CLI construct (managed C++) and a leftover from the Windows platform. In Mono, the exception propagation pattern from Mono to V8 is captured in https://github.com/tjanczuk/edge/blob/mono/src/mono/clrfunc.cpp#L64-L67. The problem with ClrFunc::MarshalCLRToV8 is that none of of the functions it calls propagate the exceptions back to ClrFunc::MarshalCLRToV8, and hence the Mono exception, if thrown, is not converted to a V8 exception. Does this make sense?

Collaborator

bmavity commented Apr 7, 2014

Yep, makes sense. We'll see if I can manage to finish over the next couple of days.

Owner

tjanczuk commented Apr 8, 2014

@joncham I started looking at the NodejsFunc implementation, which is clearly not complete. I think you intended for the NodejsFunc native class to be a closure over the persistent V8 function instance and to be coupled with NodejsFunc managed class which is a closure over the managed Func delegate exposed to managed code. What is the best Mono mechanism to capture the pointer to the native NodejsFunc instance within the managed NodejsFunc instance and then invoke a native NodejFunc method that calls into V8 Function?

Collaborator

joncham commented Apr 8, 2014

Sorry for being MIA. Looking over things again. @tjanczuk I believe my intent was to store the native NodeJSFunc in the 'native' field of the managed NodeJSFunc: https://github.com/tjanczuk/edge/blob/mono/src/mono/monoembedding.cs#L184

We can either do this by using the mono embedding API to set that field when constructing the native NodeJSFunc https://github.com/tjanczuk/edge/blob/mono/src/mono/nodejsfunc.cpp#L43 or by passing the value as a constructor parameter that we would invoke.

The easiest way to invoke the native NodeJSFunc from managed code is to register and call an internal call from managed code. See what we do for ClrFuncInvokeContext. CompleteOnCLRThreadICall:

https://github.com/tjanczuk/edge/blob/mono/src/mono/monoembedding.cs#L159

https://github.com/tjanczuk/edge/blob/mono/src/mono/monoembed.cpp#L32

https://github.com/tjanczuk/edge/blob/mono/src/mono/clrfuncinvokecontext.cpp#L76

Owner

tjanczuk commented Apr 8, 2014

@joncham This is perfect, thanks. How does mono_add_internal_call determine marshaling logic? Is it based on managed method signature?

Collaborator

joncham commented Apr 8, 2014

@tjanczuk no marshaling is performed for internal calls. You get the raw managed parameters. For example, you get a MonoString* (pointer to a structure) rather than a char_/whcar_t_ for string parameters.

Owner

tjanczuk commented Apr 9, 2014

I enabled the ability to export a function from JS to Mono with c58f370. @joncham, it would be great if you could code review.

With this change (and a few cross-platform test fixes) most tests are now passing. @bmavity, majority of remaining test failures is due to lack of proper exception handling which I hope your change will address.

@joncham, there is one test failure that I think indicates something much deeper, I wonder if you have a clue. Basically when I export a lambda expression with a closure over Mono state and later call into that lambda like this:

var edge = require('../edge');
    , assert = require('assert');

var func = edge.func(function () {/*
    async (input) =>
    {
        var k = (int)input;
        return (Func<object,Task<object>>)(async (i) => { return ++k; });
    }
*/});

var lambda = func(12, true);

assert.equal(lambda(null, true), 13); // <<< exception

The invocation of the lambda expression results in an InvalidProgramException:

System.InvalidProgramException: Invalid IL code in Startup/<Invoke>c__async0/<Invoke>c__async1/<Invoke>c__async2:MoveNext (): IL_0015: add       


  at System.Runtime.CompilerServices.AsyncTaskMethodBuilder`1[System.Object].Start[<Invoke>c__async2] (<Invoke>c__async2& stateMachine) [0x00000] in <filename unknown>:0 
  at Startup+<Invoke>c__async0+<Invoke>c__async1.<>m__1 (System.Object i) [0x00000] in <filename unknown>:0 
  at MonoEmbedding.CallFunc (System.Func`2 func, System.Object payload) [0x00000] in <filename unknown>:0 

The interesting part is that if you change the return ++k; in the C# lambda above to return 1;, the call to the lambda expression works just fine (of course it returns 1 instead of 13). This seems to indicate there is something busted about the closure of the async lambda expression which is supposed to contain k but does not.

Does this ring a bell?

Owner

tjanczuk commented Apr 9, 2014

OK, I now have evidence this is likely a Mono issue (or an issue with how we run Mono in Edge). Run this code:

var edge = require('../edge');

var func = edge.func(function () {/*
    async (input) =>
    {
        var k = (int)input;
        var f = new Func<object,Task<object>>(async (i) => { return ++k; });

        try {
            f(12);
        }
        catch (Exception e) {
            Console.WriteLine("MONO IS BUSTED: " + e.Message);
        }

        return f;
    }
*/});

var lambda = func(12, true);

And observe the message:

MONO IS BUSTED: Invalid IL code in Startup/<Invoke>c__async0/<Invoke>c__async1/<Invoke>c__async2:MoveNext (): IL_0015: add  
Owner

tjanczuk commented Apr 9, 2014

@joncham, I think I confirmed this is a Mono issue with a standalone repro:

using System;
using System.Threading.Tasks;

public class HelloWorld
{
    static public void Main ()
    {
        var k = 3;
        var f = new Func<object,Task<object>>(async (i) => { return ++k; });
        f(12); // throws "System.InvalidProgramException: Invalid IL code in HelloWorld/<Main>c__async0:MoveNext (): IL_0014: add"
    }
}

Compile and run:

gmcs -sdk:4.5 hello.cs
mono hello.exe

All details, including IL, are now at https://gist.github.com/tjanczuk/10226600
Ideas?

Owner

tjanczuk commented Apr 9, 2014

@paulcbetts What exactly does --enable-nls-no do when compiling Mono?

Collaborator

bmavity commented Apr 9, 2014

@tjanczuk Before I go further, I want to check that I am using an acceptable approach. Basically, I am passing a MonoException out parameter to each MonoEmbedding calls.

This is a function call:

else if (klass == datetime_class)
{
    double value = MonoEmbedding::GetDateValue(netdata, &exc);
    jsdata = v8::Date::New(value);
}

With the method implementation:

double MonoEmbedding::GetDateValue(MonoObject* dt, MonoException** exc)
{
    static MonoMethod* method;

    if (!method)
        method = mono_class_get_method_from_name(MonoEmbedding::GetClass(), "GetDateValue", -1);

    void* args[] = { mono_object_unbox(dt) };

    return *(double*)mono_object_unbox(mono_runtime_invoke(method, NULL, args, (MonoObject**)&exc));
}
Collaborator

joncham commented Apr 9, 2014

@bmavity first, in the call to mono_runtime_invoke exc is already a MonoException**. You don't need to dereference, i.e.

return *(double*)mono_object_unbox(mono_runtime_invoke(method, NULL, args, (MonoObject**)exc));

I think this is fine if there are not a lot of cases. The alternative is to throw a C++ exception containing the MonoException*, catch at the outter functions, etc. It's a good bit more work and I am not sure whether it's warranted. I would try with what you have and if it turns ugly we can re-evaluate.

Collaborator

bmavity commented Apr 9, 2014

@joncham Thanks. I will move forward.

Owner

tjanczuk commented Apr 9, 2014

+1 on staying with out params.

@joncham so it looks like async lambda expression closures are indeed hosed in the version of Mono we use (3.0.7): https://twitter.com/cdhowie/status/453914717694619648. I tried to compile a higher version of Mono which supposedly does not have this issue for 64 bit on MacOS but failed. I think @paulcbetts alluded before we needed 3.0.7 because later versions broke the --enable-nls=no compiler option. Are you aware of a way to compile higher version of Mono for 64 bit on Mac?

Owner

tjanczuk commented Apr 9, 2014

I've also run my mini stress test at stress/test.js. We are leaking memory like a firehose, so I will dig into that now.

Owner

tjanczuk commented Apr 9, 2014

I've fixed a number of memory leaks with bf8215c. My stress test now shows stable memory usage.

The low hanging fruit was leaking UTF16 strings during conversion from Mono to V8: bf8215c#diff-7f63c2b73bc52e12435d94011ee48dfcR8

There was also a number of issues related to not releasing GC roots from native code which prevented collection of managed Mono objects.

Owner

tjanczuk commented Apr 10, 2014

Thanks @bmavity for the exception fixes in #116. The one last remaining test failure related to exceptions required a bit more involved fix since that exception needed to be handed over across threads: e77db8a.

Fortunately with everything that had been done so far we are now really really close:

  66 passing (24s)
  1 failing

The remaining failure is due to the Mono 3.0.7 bug with lambda closures mentioned before. I will next investigate what can be done about it. I am also doing a spike on Ubuntu.

@bmavity It would be great if you could review the V8->CLR marshaling logic for error propagation.

Owner

tjanczuk commented Apr 11, 2014

Folks, this is it:

  67 passing (35s)

We have all tests passing now. I had to upgrade to Mono 3.4.0 which contains a fix to the async lambda closure bug. Unfortunately, the tarball of the sources for that Mono version at http://download.mono-project.com/sources/mono/mono-3.4.0.tar.bz2 (sha1: bae86f50f9a29d68d4e1917358996e7186e7f89e) is missing one file (how on Earth can a distribution miss a file and not fix it?), so the brew formula for installing will need to be augmented to slipstream the missing bit. I will work on this next.

Collaborator

bmavity commented Apr 11, 2014

@tjanczuk I will look at the V8->CLR error propagation over the weekend. Congrats on getting all tests to pass, I will hopefully see that happen too once I upgrade my mono install.

Collaborator

glennblock commented Apr 11, 2014

w00t! Congrats Tomek! This is a great milestone!

As a side note, the work here has really nudged us on the scriptcs side and
we are now making good progress on our mono support as well!

Keep it up!

On Friday, April 11, 2014, Brian Mavity notifications@github.com wrote:

@tjanczuk https://github.com/tjanczuk I will look at the V8->CLR error
propagation over the weekend. Congrats on getting all tests to pass, I will
hopefully see that happen too once I upgrade my mono install.

Reply to this email directly or view it on GitHubhttps://github.com/tjanczuk/edge/issues/3#issuecomment-40217197
.

Collaborator

joncham commented Apr 11, 2014

Yes, awesome job @tjanczuk and @bmavity

Owner

tjanczuk commented Apr 11, 2014

@joncham, we would be nowhere without your initial Mono effort, thanks!

Owner

tjanczuk commented Apr 11, 2014

Ubuntu: check. All tests passing.

image

I will put together instructions for Ubuntu shortly.

Collaborator

glennblock commented Apr 11, 2014

W00t!

On Fri, Apr 11, 2014 at 3:16 PM, Tomasz Janczuk notifications@github.comwrote:

Ubuntu: check. All tests passing.

[image: image]https://cloud.githubusercontent.com/assets/822369/2685247/d62dd6a8-c1c6-11e3-879b-92af69a5549d.png

I will put together instructions for Ubuntu shortly.

Reply to this email directly or view it on GitHubhttps://github.com/tjanczuk/edge/issues/3#issuecomment-40258740
.

woloski commented Apr 11, 2014

this is awesome... been following the thread for a long time. lots of cool scenarios for us running node on linux :)

On Fri, Apr 11, 2014 at 7:16 PM, Tomasz Janczuk notifications@github.com
wrote:

Ubuntu: check. All tests passing.
image

I will put together instructions for Ubuntu shortly.

Reply to this email directly or view it on GitHub:
#3 (comment)

Owner

tjanczuk commented Apr 11, 2014

Ubuntu installation instructions are here: https://github.com/tjanczuk/edge/tree/mono#building-on-ubuntu. It would be great if someone could validate them.

Collaborator

bmavity commented Apr 11, 2014

Very exciting. :) Great work everyone.

Owner

tjanczuk commented Apr 12, 2014

I updated the checked in brew formula to install Mono 3.4.0 instead of 3.0.7. @bmavity, can you run through the MacOS install instructions to get a clean build? All tests should then pass.

Collaborator

bmavity commented Apr 12, 2014

All tests passing on OSX.

Collaborator

joncham commented Apr 12, 2014

For running on OSX, why is there a requirement for uninstalling all other mono installs? This is the main blocking point for me, as I need others installed for my daily work. If there is no easy workaround I'll just setup my Ubuntu VM and work on Linux if needed rather than OSX.

Owner

tjanczuk commented Apr 12, 2014

I am not sure what would happen when multiple versions were present. One of them clearly needs to be "the default" on the path, right?


From: Jonathan Chambersmailto:notifications@github.com
Sent: ý4/ý12/ý2014 8:05 AM
To: tjanczuk/edgemailto:edge@noreply.github.com
Cc: Tomasz Janczukmailto:tomasz@janczuk.org
Subject: Re: [edge] Support for Mono (#3)

For running on OSX, why is there a requirement for uninstalling all other mono installs? This is the main blocking point for me, as I need others installed for my daily work. If there is no easy workaround I'll just setup my Ubuntu VM and work on Linux if needed rather than OSX.


Reply to this email directly or view it on GitHubhttps://github.com/tjanczuk/edge/issues/3#issuecomment-40282595.

Collaborator

glennblock commented Apr 12, 2014

I had multiple work for me as I am using Xamarin studio for some work
stuff. I started off clearing and installing 64 bit. Then I ended up
installing 32 because XS requires it. It works fine with both.

I didn't try the reverse ie installing 64 after 32, though I suspect it
works fine.

On Saturday, April 12, 2014, Tomasz Janczuk notifications@github.com
wrote:

I am not sure what would happen when multiple versions were present. One
of them clearly needs to be "the default" on the path, right?


From: Jonathan Chambersmailto:notifications@github.com<javascript:_e(%7B%7D,'cvml','notifications@github.com');>

Sent: ý4/ý12/ý2014 8:05 AM
To: tjanczuk/edgemailto:edge@noreply.github.com<javascript:_e(%7B%7D,'cvml','edge@noreply.github.com');>

Cc: Tomasz Janczukmailto:tomasz@janczuk.org<javascript:_e(%7B%7D,'cvml','tomasz@janczuk.org');>

Subject: Re: [edge] Support for Mono (#3)

For running on OSX, why is there a requirement for uninstalling all other
mono installs? This is the main blocking point for me, as I need others
installed for my daily work. If there is no easy workaround I'll just setup
my Ubuntu VM and work on Linux if needed rather than OSX.

Reply to this email directly or view it on GitHub<
https://github.com/tjanczuk/edge/issues/3#issuecomment-40282595>.

Reply to this email directly or view it on GitHubhttps://github.com/tjanczuk/edge/issues/3#issuecomment-40282705
.

Thanks! The mac version works great for me!
One question: How would I handle this regarding to the package.json? Edge.js would have to go to the dependencies section, not to the devDependencies, because each user needs it (if I get that right). But how I can I make sure the mono version is being deployed, when sombody runs 'npm install' in the folder?
Is there any way to handle the deploment on windows and mac machines alike atm?

Owner

tjanczuk commented Apr 15, 2014

You will normally list edge in the dependencies section of package.json. However, npm install of edge requires that prerequisities are met:

  • On Windows, one must have .NET Framework 4.5 installed. In practice this is is true across all new Windows OSes out of the box.
  • On MacOS, someone must manually compile and install Mono x64. We will be providing a Homebrew formula to make it easy (I assume you have seen the instructions).
  • On Linux, someone must manually comile and install Mono x64. The process will vary depending on Linux distro. I am preparing a bash script that will set up Node, Mono, and Edge on an Ubuntu box in an "unattended" fashion.

Bottom line is npm install can only succeed once the prerequisites are satisfied.

Owner

tjanczuk commented Apr 15, 2014

Folks, I streamlined the Ubuntu installation experience with a single script. It would be great if someone other than me could guinea pig this: https://github.com/tjanczuk/edge/tree/mono#ubuntu-starting-from-a-clean-vm-ie-taking-the-high-road.

I think we are getting close to a point where I want to pull this work into master and start working on the last pre-release rites.

Collaborator

bmavity commented Apr 15, 2014

@tjanczuk Sorry, got swamped with work. Is the exception handling the only thing holding this up now? I will try to get to it in the next couple of days either way.

Owner

tjanczuk commented Apr 15, 2014

@bmavity no worry. Depending how timing works out you can PR against master.

Owner

tjanczuk commented Apr 17, 2014

I merged mono branch into master. @bmavity if you get to working on the exception handling, please make your PR against master. I refactored files for better code reuse across platforms - directory structure under src has changed.

@joncham do you know of any reason tests would take ~8 seconds on Windows and Ubuntu but ~40 seconds on a Mac (where Ubuntu and Windows are VMs on the very Mac)? Is Mono dog slow on Mac?

Collaborator

joncham commented Apr 17, 2014

Historically mono on OSX had some performance issues but I haven't tracked the status of them all.

  • What GC are we using? i.e. which mono dynamic library do we load, the boehm one or the sgen one?
  • Are we 64-bit on both OSX and Linux?
  • I believe threading primitives for synchronization have performed worse on OSX historically. That could also be part of the issue.

You can use Instruments to find out what is making the code slow.

Boehm is a lot slower on OSX/iOS than SGen is, in particular with anything involving threads due to the different allocation strategies and locking they use.

I use the SGen version of Mono 3.4.0 and it takes 41 seconds to run all 67 tests.

I bet we have something that does not run great on OSX. Keep us posted!

Owner

tjanczuk commented Apr 17, 2014

Both Ubuntu and MacOS are running sgen in my case. I think this is unlikely related to GC, as the 67 tests that run are all functional with not a lot of memory allocation that would put strain on GC. Let me see if Instruments shed some light on this.

Owner

tjanczuk commented Apr 17, 2014

I've run Time Profiler on my stress test on MacOS. Below are the top functions where time is spent. If I interpret the data correctly (first time I use Time Profiler), calls to mono_runtime_invoke end up spinning new threads with a call to thread_start, which is where 64.6% of time is spent. @migueldeicaza @joncham is that a correct interpretation and expected behavior? Why is Mono creating a new thread every time I call into managed code, and is there a way to avoid it?

image

Collaborator

joncham commented Apr 17, 2014

  1. This indicates Boehm is being used rather than SGen (libmonoboehm-2.0.1.dylib).
  2. I haven't used Time Profiler much but I am fairly certain that a thread should not be created for each managed invoke. I am not able to interpret the results in a meaningful way though.
  3. If the overhead of the mono_runtime_invoke itself is slow we can cache some function pointers for the managed methods and invoke them that way instead. However, given the performance difference between Linux and OSX I doubt this is the bottleneck.

Mono is being built on OSX via the script helper, right? I assume it's built in release with optimizations, etc?

Collaborator

joncham commented Apr 17, 2014

Try reference monosgen-2 instead of mono-2 here:

https://github.com/tjanczuk/edge/blob/master/binding.gyp#L57

I assume that is used for the OSX and Linux builds.

Collaborator

joncham commented Apr 17, 2014

Assuming I set everything up correctly on OSX and switched successfuly, I get very similar results for both boehm and sgen based monos. ~44s for each to run the tests.

Owner

tjanczuk commented Apr 17, 2014

I compiled with monosgen-2 instead of mono-2. This seems to have shaved off a few seconds from the run (38 seconds instead of 42), but it is still a far cry from Ubuntu or Windows. Is SGen recommended for both OSX and Ubuntu? Why is bohem the default?

My stress run still shows the same strange thread-creating behavior of mono_runtime_invoke:

image

Mono is building on MacOS via the brew formula, and on Ubuntu via the install script. In both cases compilation options are the same.

BTW, I did not realize the GC technology depends on how edge.node was linked. I thought it was a function of how Mono was linked (i.e. the GC reported by mono -V).

Owner

tjanczuk commented Apr 17, 2014

I know this is a far shot, but here is one question in my mind: somewhere in the Mono documentation I came across the notion of "registering all threads on which managed code runs with the Mono runtime". I believe there is an API for that. I don't see us calling that API from the V8 thread anywhere. Is this perhaps something that may be related to that new thread being created here, given that mono_runtime_invoke is usually called from the V8 thread?

Owner

tjanczuk commented Apr 18, 2014

I just realized the call stacks are inverted here, so it is not mono_runtime_invoke spinning new threads, it is mono_runtime_invoke being called from a new thread.

Owner

tjanczuk commented Apr 18, 2014

@migueldeicaza @joncham is there some known performance issue around Mono's CSharpCodeProvider on MacOS?

I investigated this further and it appears that compiling C# code into an in-memory assembly on the fly using CSharpCodeProvider in Mono is taking 4x longer on MacOS than on Ubuntu (1200ms vs 300ms for the same piece of C# code). This explains why the tests are taking ~4x longer to run on MacOS than Ubuntu or Windows - almost every test is compiling a piece of C#.

Once the managed code is compiled, calling Mono from Node.js in-process has pretty much identical performance between MacOS and Ubuntu.

So this is a startup latency issue (since most apps would compile C# code once on startup and then use it), which is less worrisome than a steady state performance problem would be. Still, it would good to get the bottom of this. The piece of code that does the compilation is here.

I wrote a simple latency test at https://github.com/tjanczuk/edge/blob/master/performance/latency.js that allows one to call C# code from Node.js in a sequential loop in either of two modes:

  1. Compile once, call N times, and average out results.
  2. Compile N times and call each func 1 time, then average the results.

The first variant performs the same on Mac and Ubuntu for a large N (100,000). The second variant is 4x slower on Mac.

Owner

tjanczuk commented Apr 18, 2014

@migueldeicaza @joncham Here is a standalone, Mono-only repro of the performance issue with CSharpCodeProvider on MacOS: https://gist.github.com/tjanczuk/11026840

Compile and run like this:

mcs -sdk:4.5 compile.cs
mono compile.exe 20

Results on Mac with Mono 3.4.0 x64:

Total [ms]: 10182, average [ms]: 509

Results on Ubuntu with Mono 3.4.0 x64:

Total [ms]: 2584, average [ms]: 129

So, Mac is ~4x slower.

Owner

tjanczuk commented Apr 18, 2014

The issue appears to be in the Mono compiler itself rather than CSharpCodeProvider.

On Mac:

Total(ms) Self(ms)      Calls Method name
    9787        3         20 Mono.CSharp.CSharpCodeCompiler:CompileFromFileBatch (System.CodeDom.Compiler.CompilerParameters,string[])
System.Diagnostics.Process:WaitForExit ()
    9682        0         20 System.Diagnostics.Process:WaitForExit ()

On Ubuntu:

Total(ms) Self(ms)      Calls Method name
    2677        2         20 Mono.CSharp.CSharpCodeCompiler:CompileFromFileBatch (System.CodeDom.Compiler.CompilerParameters,string[])
System.Diagnostics.Process:WaitForExit ()
    2597        0         20 System.Diagnostics.Process:WaitForExit ()

Could you dump all the parameters you pass to the C# compiler?

It would be interesting to compare the time of the compiler to build on Mac and Linux, without the use of the Microsoft.CSharp API (which calls into Process, which could be slower on OSX).

Owner

tjanczuk commented Apr 21, 2014

Here they are: https://gist.github.com/tjanczuk/11026840#file-compile-cs-L20-L27. I am not quite sure how the CompilerParameters translate to command line options to the compiler.

Ok, we need a repro test case ;-)

Let us do this: run the compile and use "time" to measure the execution time,

something like this would work:

time myScript

myScript contains:

#!/bin/sh
for i in 1 2 3 4 5 6 7 8 9 10 11; do
mcs file1.cs file2.cs -r:System.Xml
done

Owner

tjanczuk commented Apr 21, 2014

Oh I see, you want to isolate mcs. Sorry I did not read through.

Owner

tjanczuk commented Apr 21, 2014

Here is it.

source.cs:

using System;
using System.Threading.Tasks;

public class Startup {
    public async Task<object> Invoke(object ___input) {
        Func<object, Task<object>> func = async (input) => { return 12; };
        return await func(___input);
    }
}

compile.sh:

#!/bin/bash
for i in {1..20}
do
    mcs -sdk:4.5 -target:library source.cs -r:System.dll -r:System.Core.dll -r:Microsoft.CSharp.dll
done

Running time ./compile.sh on MacOS:

real    0m9.660s
user    0m9.141s
sys 0m0.457s

The same on Ubuntu 12.04 (a VM on the same Mac):

real    0m2.625s
user    0m1.969s
sys 0m0.585s

I suspect this is not even a compiler issue;

I suspect this is a Mono startup on OSX issue. Can you repeat this experiment on your configuration for "Hello World"?

Do you have AOT libraries on Ubuntu?

Owner

tjanczuk commented Apr 22, 2014

I think you are right.

I compiled this piece with mcs -sdk:4.5 hello.cs:

using System;

public class HelloWorld
{
    static public void Main ()
    {
        Console.WriteLine("DONE");
    } 
}

Then I run it 100 times with a script:

#!/bin/bash
for i in {1..100}
do
    mono hello.exe
done

And timed with time ./run.sh.

On MacOS:

real    0m3.068s
user    0m2.371s
sys 0m0.594s

On Ubuntu:

real    0m1.175s
user    0m0.540s
sys 0m0.555s

Regarding AOT, yes I do have the AOT for mcs.exe and mscorlib.dll on Ubuntu. I don't have AOTs on MacOS. I don't know how or when AOTs were generated on Ubuntu: I followed the exact same set of setup steps when compiling Mono from sources.

Anyhow, after manually deleting the AOTs on Ubuntu and running the test again, it performs slower than before, but still much faster than MacOS:

real    0m2.302s
user    0m1.384s
sys 0m0.804s

Interestingly, generating AOTs on MacOS with mono --aot=full /usr/local/Cellar/mono64/3.4.0/lib/mono/4.5/mscorlib.dll and running the test again does not seem to affect the results on Mac.

Please do not use --full; That is intended solely for static compilation and has all kinds of downsides.

So this explains a large part of the mystery.

Owner

tjanczuk commented Apr 22, 2014

I think 3 questions remain:

  1. Even with AOT removed from Ubuntu, it is still 30% faster than MacOS.
  2. Even with AOT for mscorlib present on MacOS, MacOS is no faster than without it (unless I created it in a wrong way).
  3. Should AOT be created during make & make install, and if so, why was it created on Ubuntu but not MacOS.

AOT is an optional step, some Linux distros use it, some do not.

We do not do it ourselves on OSX, but we might enable it at one point.

Owner

tjanczuk commented Apr 22, 2014

Sounds good, thanks for your help.

Owner

tjanczuk commented Apr 24, 2014

As one of the last steps of validation I verified we can now develop & test on Mac and deploy to Windows on Windows Azure Web Sites (or is that thing now called Microsoft Azure Web Sites?).

server.js:

var func = require('edge').func(function () {/*
    async (input) => {
        return ".NET welcomes " + input.ToString();
    }
*/});

require('http').createServer(function (req, res) {
    func('Node.js', function (error, result) {
        if (error) throw error;
        res.writeHead(200, { 'Content-Type': 'text/plain' });
        res.end(result + '\n');        
    });
}).listen(process.env.PORT || 8080);

package.json:

{
  "name": "edge080",
  "version": "0.0.1",
  "dependencies": {
    "edge": "https://github.com/tjanczuk/edge/archive/master.tar.gz"
  }
}

testing on MacOS with Mono:

image

Then using azure-cli to deploy to Windows Azure Web Sites (running on Windows x64 with .NET), and voila:

image

Owner

tjanczuk commented Apr 24, 2014

Functional tests passing on Windows, Ubuntu 12.04, and MacOS. Stress test rock solid after 24h on all three platforms.

Owner

tjanczuk commented Apr 29, 2014

I think we are done here.

We have shipped Edge.js 0.8.0 with support for Mac OS and Linux via Mono on NPM about 43 seconds ago.

This would not have happened without you. Thank you (in gitalphabetical order):

@7sharp9 @paulcbetts for ongoing technical and moral support
@bmavity for re-igniting the effort and contributing code
@dragan for making the right connections
@glennblock for enthusiastic evangelism, technical, and moral support
@joncham for the fundamental spike on Mono
@migueldeicaza for Mono expertise (and for Mono ;)
@mythz for the initial spark

I am now closing this issue. Let's drive fixups through separate issues.

tjanczuk closed this Apr 29, 2014

Collaborator

glennblock commented Apr 29, 2014

Fantastic!!!!!!!!!!!!!!!

On Tue, Apr 29, 2014 at 1:20 AM, Tomasz Janczuk notifications@github.comwrote:

Closed #3 #3.


Reply to this email directly or view it on GitHubhttps://github.com/tjanczuk/edge/issues/3
.

Collaborator

glennblock commented Apr 29, 2014

@tjanczuk this effort has also really helped give scriptcs a push to get our mono support as well. We're so close. So double thanks!

Owner

tjanczuk commented Apr 29, 2014

@glennblock we should sit down and plan next moves. I think scriptcs + edge.js + #17 + cross-platform would result in ultimate delirium

Collaborator

glennblock commented Apr 29, 2014

Yes!

I still have this that needs to be packaged up.
https://github.com/glennblock/edge-scs. We do have scriptcs now working on
mono so it's doable to start doing the work.

I might just need your help in figuring out the best option of how to
package.

On Tue, Apr 29, 2014 at 1:43 AM, Tomasz Janczuk notifications@github.comwrote:

@glennblock https://github.com/glennblock we should sit down and plan
next moves. I think scriptcs + edge.js + #17https://github.com/tjanczuk/edge/issues/17\+ cross-platform would result in ultimate delirium


Reply to this email directly or view it on GitHubhttps://github.com/tjanczuk/edge/issues/3#issuecomment-41653417
.

I can able run on my local machine with edge.js.When I will deploy into the server it is giving an error."casttype is not found".

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment