Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Unexpected behavior #219

Open
diegodfsd opened this issue Jun 17, 2020 · 3 comments
Open

Unexpected behavior #219

diegodfsd opened this issue Jun 17, 2020 · 3 comments

Comments

@diegodfsd
Copy link

diegodfsd commented Jun 17, 2020

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;
      }
    }
@nilproject
Copy link
Owner

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.

@Markeli
Copy link
Contributor

Markeli commented Jan 17, 2023

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?

@nilproject
Copy link
Owner

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

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

3 participants