NiL.JS icon indicating copy to clipboard operation
NiL.JS copied to clipboard

Unexpected behavior

Open diegodfsd opened this issue 5 years ago • 3 comments

Hi,

I'm using NiL.JS version 2.5.1294 with .net core 3.1.201 in production and in last days we caught the exception below. Does anyone have any ideas to help us to find the cause?

Operations that change non-concurrent collections must have exclusive access. A concurrent update was performed on this collection and corrupted its state. The collection's state is no longer correct.

System.InvalidOperationException: Operations that change non-concurrent collections must have exclusive access. A concurrent update was performed on this collection and corrupted its state. The collection's state is no longer correct.
   at System.Collections.Generic.Dictionary`2.FindEntry(TKey key)
   at System.Collections.Generic.Dictionary`2.TryGetValue(TKey key, TValue& value)
   at NiL.JS.Core.Functions.MethodProxy..ctor(Context context, MethodBase methodBase, Object hardTarget)
   at NiL.JS.Core.Interop.Proxy.proxyMember(Boolean forWrite, IList`1 m)
   at NiL.JS.Core.Interop.Proxy.GetProperty(JSValue key, Boolean forWrite, PropertyScope memberScope)
   at NiL.JS.Core.JSObject.GetProperty(JSValue key, Boolean forWrite, PropertyScope propertyScope)
   at NiL.JS.Core.JSObject.SetProperty(JSValue key, JSValue value, PropertyScope propertyScope, Boolean throwOnError)
   at NiL.JS.Expressions.SetProperty.Evaluate(Context context)
   at NiL.JS.Statements.CodeBlock.evaluateLines(Context context, Int32 i, Boolean clearSuspendData)
   at NiL.JS.Statements.CodeBlock.Evaluate(Context context)
   at NiL.JS.Statements.TryCatch.<>c__DisplayClass16_0.<catchHandler>b__0(Context c)
   at NiL.JS.Statements.TryCatch.Evaluate(Context context)
   at NiL.JS.Statements.CodeBlock.evaluateLines(Context context, Int32 i, Boolean clearSuspendData)
   at NiL.JS.Statements.CodeBlock.Evaluate(Context context)
   at NiL.JS.BaseLibrary.Function.evaluateBody(Context internalContext)
   at NiL.JS.BaseLibrary.Function.Invoke(Boolean construct, JSValue targetObject, Arguments arguments)
   at <delegate>(Closure , String )

We create a wrapper over Context and to call a js method we have:

public Func<T, T> Call<T>(string varName, JsFunctionCallContext<T> callContext = null)
        {
            if (callContext != null)
                _context.DefineVariable("context").Assign(JSValue.Marshal(callContext));

            var function = _context.GetVariable(varName).As<Function>();

            return (Func<T, T>) function.MakeDelegate(typeof(Func<T, T>));
        }

This is our JsFunctionCallContext used in js function context.

public class JsFunctionCallContext<TResult>
    {
        public HttpResponse ResponseRaw { get; }
        public object Extras { get; }
        public bool Success { get; set; }
        public bool HasNexpPage { get; set; }
        public TResult Result { get; set; }
        public string ErrorMessage { get; set; }

        public JsFunctionCallContext(HttpResponse responseRaw = null, object extras = null)
        {
            Extras = extras;
            ResponseRaw = responseRaw ?? new HttpResponse();
        }
    }

Usually the js function works as a map to transform a json data.

var transform = function(response) {      
      try {
        const data = JSON.parse(response);                
          return ({
              newName: data.name,
            });
        context.Success = true;
        context.ErrorMessage = '';
        context.Result = JSON.stringify(payload);
      } catch(error) {
        context.Success = false;
        context.ErrorMessage = error.message;
      }
    }

diegodfsd avatar Jun 17 '20 23:06 diegodfsd

Hi, @diegodfsd. Sorry for late reply. I guess, in your code the scripts are executed in multiple threads. Alas, the library not supported this scenario. To solve this, you need to either use synchronization or use a separate GlobalContext.

nilproject avatar Jul 21 '20 12:07 nilproject

Sometimes we have got the same exception. We create unique module for each HTTP request but we reuse the same GlobalContext in group of HTTP request. Is it correct? Or should we create unique GlobalContext for each HTTP request?

Markeli avatar Jan 17 '23 16:01 Markeli

Depends on what code will be executed in that context. If it is guaranteed not to change a state of global objects and prototypes of proxied types, then you can use one for all requests. For example, if your product is web browser, it's required to create new GlobalContext per page and evaluate scripts only in one thread in the same time. But if your product is something like application with dynamically load modules, and they are yours too, you can use one GlobalContext for all requests. In one thread, of course, there is no exceptions

nilproject avatar Jan 17 '23 18:01 nilproject