Hello,

I am trying to figure out how to maintain state associated with async 
functions across await boundaries. We currently have instrumentation for 
promises which allows us to maintain state in each step of the promise 
chain, unfortunately for us async/await is implemented using only internal 
functions with no exposure to JS-land. This makes it impossible for us to 
instrument it without requiring either user or V8 code modification. The 
app pasted below shows a simple use case where using promises directly 
maintains the correct state while using async/await loses it.

I was exploring this using Node 7.7.1 which uses V8 5.5.372 and found a 
naive solution (see attached await.patch). This shows the spirit of what we 
would like to do. We need async/await to use a JS-land function to add its 
continuation, or otherwise expose some kind of hook, so that we can get in 
there and maintain our state. I looked into upgrading my patch to the 
latest V8 before bringing it here to discuss, but it looks like async/await 
was completely refactored and the JS function AsyncFunctionAwait which my 
patch modified became the C++ function AsyncBuiltinsAssembler::Await (in 
builtins-async.cc) and I am not at all familiar with V8's assemblers.

I would like to know what is the best way forward to get a change into V8 
making async/await instrumentable?

-Bryan

'use strict';

// Some environment variables to make newrelic keep quiet.
process.env.NEW_RELIC_NO_CONFIG_FILE = true;
process.env.NEW_RELIC_APP_NAME = 'async await example';
process.env.NEW_RELIC_LICENSE_KEY = 'this is not a license';

var newrelic = require('newrelic');
var Promise = require('bluebird');
var fs = Promise.promisifyAll(require('fs'));
var path = require('path');

var pkgPath = path.resolve(__dirname, './package.json');

// A simple async function to show transaction state loss.
var asyncAwait = newrelic.createBackgroundTransaction('asdf', async 
function() {
  console.log('async', !!newrelic.agent.tracer.segment);
  await fs.readFileAsync(pkgPath, 'utf8'); // <-- State lost here.
  console.log('async', !!newrelic.agent.tracer.segment);

  // Just to show pure promises keeps state.
  promises();
});

// Same thing but just with promises.
var promises = newrelic.createBackgroundTransaction('asdf', function() {
  console.log('promises', !!newrelic.agent.tracer.segment);
  return fs.readFileAsync(pkgPath, 'utf8').then(() => {
    console.log('promises', !!newrelic.agent.tracer.segment);
    process.exit(0);
  });
});

asyncAwait();

-- 
-- 
v8-dev mailing list
[email protected]
http://groups.google.com/group/v8-dev
--- 
You received this message because you are subscribed to the Google Groups 
"v8-dev" group.
To unsubscribe from this group and stop receiving emails from it, send an email 
to [email protected].
For more options, visit https://groups.google.com/d/optout.
diff --git a/deps/v8/src/js/async-await.js b/deps/v8/src/js/async-await.js
index b733f3d9fa..88883157e4 100644
--- a/deps/v8/src/js/async-await.js
+++ b/deps/v8/src/js/async-await.js
@@ -91,27 +91,15 @@ function AsyncFunctionAwait(generator, awaited, outerPromise) {
     return;
   }
 
-  // Just forwarding the exception, so no debugEvent for throwawayCapability
-  var throwawayCapability = NewPromiseCapability(GlobalPromise, false);
-
-  // The Promise will be thrown away and not handled, but it shouldn't trigger
-  // unhandled reject events as its work is done
-  SET_PRIVATE(throwawayCapability.promise, promiseHasHandlerSymbol, true);
-
   if (DEBUG_IS_ACTIVE) {
     if (IsPromise(awaited)) {
       // Mark the reject handler callback to be a forwarding edge, rather
       // than a meaningful catch handler
       SET_PRIVATE(onRejected, promiseForwardingHandlerSymbol, true);
     }
-
-    // Mark the dependency to outerPromise in case the throwaway Promise is
-    // found on the Promise stack
-    SET_PRIVATE(throwawayCapability.promise, promiseHandledBySymbol,
-                outerPromise);
   }
 
-  PerformPromiseThen(promise, onFulfilled, onRejected, throwawayCapability);
+  promise.then(onFulfilled, onRejected);
 }
 
 // Called by the parser from the desugaring of 'await' when catch

Reply via email to