public static Pid StartLink(String id, Func <SupervisorConfig> init) { ErlangCallback initCallback = (ErlNifTerm args) => { var config = init(); return(Erl.MakeTuple2( Erl.MakeAtom("ok"), config.ToErlNifTerm() )); }; switch (Erl.Modules.DotnetSupervisor.StartLink(Tuple.Create(new Atom("local"), new Atom(id)), initCallback)) { case Tuple <Atom, Pid> success: return(success.Item2); case Tuple <Atom, Atom> error: throw new Exception("Failed to start supervisor with error " + error); default: throw new Exception("Failed to start supervisor for unknown reasons"); } }
private static Pid StartLinkInternal <T>(Object name, Func <T> init) { var handleInfoInterface = typeof(T).GetInterfaces() .Where(x => x.IsGenericType && x.GetGenericTypeDefinition() == typeof(IHandleInfo <>)) .FirstOrDefault(); var handleCallInterface = typeof(T).GetInterfaces() .Where(x => x.IsGenericType && x.GetGenericTypeDefinition() == typeof(IHandleCall <>)) .FirstOrDefault(); var handleCastInterface = typeof(T).GetInterfaces() .Where(x => x.IsGenericType && x.GetGenericTypeDefinition() == typeof(IHandleCast <>)) .FirstOrDefault(); var terminateInterface = typeof(T).GetInterfaces() .Where(x => x == typeof(ITerminate)) .FirstOrDefault(); ErlangCallback initCallback = (ErlNifTerm obj) => { var genserver = init(); return(Erl.MakeTuple2(Erl.MakeAtom("ok"), Erl.MakeObjectReference(genserver))); }; // Yes nasty Erlang reflection, we could actually cache all of these but who has the time of day? // If you're worried about performance then that means you're actually using this code // in which case you have bigger concerns than performance ErlangCallback handleInfoCallback = (ErlNifTerm input) => { var args = Erl.Coerce <Tuple <ErlNifTerm, ErlNifTerm> >(input); var msgType = handleInfoInterface.GetGenericArguments()[0]; var msg = Erl.Coerce(args.Item1, msgType); var state = Erl.GetObjectReference(args.Item2); HandleInfoResult result = (HandleInfoResult)handleInfoInterface.GetMethod("HandleInfo").Invoke(state, new object[] { new HandleInfoContext(state), msg }); return(result.Native); }; ErlangCallback handleCallCallback = (ErlNifTerm input) => { var args = Erl.Coerce <Tuple <ErlNifTerm, ErlNifTerm, ErlNifTerm> >(input); var msgType = handleCallInterface.GetGenericArguments()[0]; var msg = Erl.Coerce(args.Item1, msgType); var sender = Erl.Coerce <Pid>(args.Item2); var state = Erl.GetObjectReference(args.Item3); HandleCallResult result = (HandleCallResult)handleCallInterface.GetMethod("HandleCall").Invoke(state, new object[] { new HandleCallContext(sender, state), msg }); return(result.Native); }; ErlangCallback handleCastCallback = (ErlNifTerm input) => { var args = Erl.Coerce <Tuple <ErlNifTerm, ErlNifTerm> >(input); var msgType = handleCastInterface.GetGenericArguments()[0]; var msg = Erl.Coerce(args.Item1, msgType); var state = Erl.GetObjectReference(args.Item2); HandleCastResult result = (HandleCastResult)handleCastInterface.GetMethod("HandleCast").Invoke(state, new object[] { new HandleCastContext(state), msg }); return(result.Native); }; ErlangCallback terminateCallback = (ErlNifTerm input) => { var args = Erl.Coerce <Tuple <ErlNifTerm, ErlNifTerm> >(input); var reason = Erl.Coerce <Atom>(args.Item1); var state = Erl.GetObjectReference(args.Item2); TerminateResult result = (TerminateResult)terminateInterface.GetMethod("Terminate").Invoke(state, new object[] { new TerminateContext(), reason }); return(result.Native); }; var callbacks = new DotNetGenServerArgs { Init = initCallback , HandleInfo = handleInfoInterface == null ? null : handleInfoCallback , HandleCall = handleCallInterface == null ? null : handleCallCallback , HandleCast = handleCastInterface == null ? null : handleCastCallback , Terminate = terminateInterface == null ? null : terminateCallback }; Object result; if (name != null) { result = Erl.Modules.DotnetGenserver.StartLink(name, callbacks); } else { result = Erl.Modules.DotnetGenserver.StartLink(callbacks); } switch (result) { case Tuple <Atom, Pid> success: return(success.Item2); case Tuple <Atom, Atom> error: throw new Exception("Got an error when starting gen server " + error.Item2); default: throw new Exception("Failed to start the gen server for unknown reasons"); } }