node/test/sequential/test-async-wrap-getasyncid.js
Anna Henningsen afdc3d0d18 dgram: use uv_udp_try_send()
This improves dgram performance by avoiding unnecessary async
operations.

One issue with this commit is that it seems hard to actually create
conditions under which the fallback path to the async case is
actually taken, for all supported OS, so an internal CLI option
is used for testing that path.

Another caveat is that the lack of an async operation means
that there are slight timing differences (essentially `nextTick()`
rather than `setImmediate()` for the send callback).

PR-URL: https://github.com/nodejs/node/pull/29832
Reviewed-By: David Carlier <devnexen@gmail.com>
Reviewed-By: James M Snell <jasnell@gmail.com>
Reviewed-By: Ben Noordhuis <info@bnoordhuis.nl>
2019-10-05 17:48:10 -07:00

309 lines
9.1 KiB
JavaScript

'use strict';
// Flags: --expose-gc --expose-internals --no-warnings --test-udp-no-try-send
const common = require('../common');
const { internalBinding } = require('internal/test/binding');
const assert = require('assert');
const fs = require('fs');
const v8 = require('v8');
const fsPromises = fs.promises;
const net = require('net');
const providers = Object.assign({}, internalBinding('async_wrap').Providers);
const fixtures = require('../common/fixtures');
const tmpdir = require('../common/tmpdir');
const { getSystemErrorName } = require('util');
// Make sure that all Providers are tested.
{
const hooks = require('async_hooks').createHook({
init(id, type) {
if (type === 'NONE')
throw new Error('received a provider type of NONE');
delete providers[type];
},
}).enable();
process.on('beforeExit', common.mustCall(() => {
// This garbage collection call verifies that the wraps being garbage
// collected doesn't resurrect the process again due to weirdly timed
// uv_close calls and other similar instruments in destructors.
global.gc();
process.removeAllListeners('uncaughtException');
hooks.disable();
delete providers.NONE; // Should never be used.
// See test/pseudo-tty/test-async-wrap-getasyncid-tty.js
// Requires an 'actual' tty fd to be available.
delete providers.TTYWRAP;
// TODO(jasnell): Test for these
delete providers.HTTP2SESSION;
delete providers.HTTP2STREAM;
delete providers.HTTP2PING;
delete providers.HTTP2SETTINGS;
// TODO(addaleax): Test for these
delete providers.STREAMPIPE;
delete providers.MESSAGEPORT;
delete providers.WORKER;
if (!common.isMainThread)
delete providers.INSPECTORJSBINDING;
delete providers.KEYPAIRGENREQUEST;
delete providers.HTTPCLIENTREQUEST;
delete providers.HTTPINCOMINGMESSAGE;
delete providers.ELDHISTOGRAM;
const objKeys = Object.keys(providers);
if (objKeys.length > 0)
process._rawDebug(objKeys);
assert.strictEqual(objKeys.length, 0);
}));
}
function testUninitialized(req, ctor_name) {
assert.strictEqual(typeof req.getAsyncId, 'function');
assert.strictEqual(req.getAsyncId(), -1);
assert.strictEqual(req.constructor.name, ctor_name);
}
function testInitialized(req, ctor_name) {
assert.strictEqual(typeof req.getAsyncId, 'function');
assert(Number.isSafeInteger(req.getAsyncId()));
assert(req.getAsyncId() > 0);
assert.strictEqual(req.constructor.name, ctor_name);
}
{
const cares = internalBinding('cares_wrap');
const dns = require('dns');
testUninitialized(new cares.GetAddrInfoReqWrap(), 'GetAddrInfoReqWrap');
testUninitialized(new cares.GetNameInfoReqWrap(), 'GetNameInfoReqWrap');
testUninitialized(new cares.QueryReqWrap(), 'QueryReqWrap');
testInitialized(dns.lookup('www.google.com', () => {}), 'GetAddrInfoReqWrap');
testInitialized(dns.lookupService('::1', 22, () => {}), 'GetNameInfoReqWrap');
const resolver = new dns.Resolver();
resolver.setServers(['127.0.0.1']);
testInitialized(resolver._handle, 'ChannelWrap');
testInitialized(resolver.resolve6('::1', () => {}), 'QueryReqWrap');
resolver.cancel();
}
{
const FSEvent = internalBinding('fs_event_wrap').FSEvent;
testInitialized(new FSEvent(), 'FSEvent');
}
{
const JSStream = internalBinding('js_stream').JSStream;
testInitialized(new JSStream(), 'JSStream');
}
{
// We don't want to expose getAsyncId for promises but we need to construct
// one so that the corresponding provider type is removed from the
// providers list.
new Promise((res) => res(5));
}
if (common.hasCrypto) { // eslint-disable-line node-core/crypto-check
const crypto = require('crypto');
// The handle for PBKDF2 and RandomBytes isn't returned by the function call,
// so need to check it from the callback.
const mc = common.mustCall(function pb() {
testInitialized(this, 'AsyncWrap');
});
crypto.pbkdf2('password', 'salt', 1, 20, 'sha256', mc);
crypto.randomBytes(1, common.mustCall(function rb() {
testInitialized(this, 'AsyncWrap');
}));
if (typeof internalBinding('crypto').scrypt === 'function') {
crypto.scrypt('password', 'salt', 8, common.mustCall(function() {
testInitialized(this, 'AsyncWrap');
}));
}
}
{
const binding = internalBinding('fs');
const path = require('path');
const FSReqCallback = binding.FSReqCallback;
const req = new FSReqCallback();
req.oncomplete = () => { };
testInitialized(req, 'FSReqCallback');
binding.access(path.toNamespacedPath('../'), fs.F_OK, req);
const StatWatcher = binding.StatWatcher;
testInitialized(new StatWatcher(), 'StatWatcher');
}
{
const { HTTPParser } = require('_http_common');
const parser = new HTTPParser();
testUninitialized(parser, 'HTTPParser');
parser.initialize(HTTPParser.REQUEST, {});
testInitialized(parser, 'HTTPParser');
}
{
const Gzip = require('zlib').Gzip;
testInitialized(new Gzip()._handle, 'Zlib');
}
{
const binding = internalBinding('pipe_wrap');
const handle = new binding.Pipe(binding.constants.IPC);
testInitialized(handle, 'Pipe');
}
{
tmpdir.refresh();
const server = net.createServer(common.mustCall((socket) => {
server.close();
})).listen(common.PIPE, common.mustCall(() => {
const binding = internalBinding('pipe_wrap');
const handle = new binding.Pipe(binding.constants.SOCKET);
testInitialized(handle, 'Pipe');
const req = new binding.PipeConnectWrap();
testUninitialized(req, 'PipeConnectWrap');
req.address = common.PIPE;
req.oncomplete = common.mustCall(() => handle.close());
handle.connect(req, req.address, req.oncomplete);
testInitialized(req, 'PipeConnectWrap');
}));
}
{
const Process = internalBinding('process_wrap').Process;
testInitialized(new Process(), 'Process');
}
{
const { Signal } = internalBinding('signal_wrap');
testInitialized(new Signal(), 'Signal');
}
{
async function openTest() {
const fd = await fsPromises.open(__filename, 'r');
testInitialized(fd, 'FileHandle');
await fd.close();
}
openTest().then(common.mustCall());
}
{
const binding = internalBinding('stream_wrap');
testUninitialized(new binding.WriteWrap(), 'WriteWrap');
}
{
const stream_wrap = internalBinding('stream_wrap');
const tcp_wrap = internalBinding('tcp_wrap');
const server = net.createServer(common.mustCall((socket) => {
server.close();
socket.on('data', () => {
socket.end();
socket.destroy();
});
socket.resume();
})).listen(0, common.localhostIPv4, common.mustCall(() => {
const handle = new tcp_wrap.TCP(tcp_wrap.constants.SOCKET);
const req = new tcp_wrap.TCPConnectWrap();
const sreq = new stream_wrap.ShutdownWrap();
testInitialized(handle, 'TCP');
testUninitialized(req, 'TCPConnectWrap');
testUninitialized(sreq, 'ShutdownWrap');
sreq.oncomplete = common.mustCall(() => {
handle.close();
});
req.oncomplete = common.mustCall(writeData);
function writeData() {
const wreq = new stream_wrap.WriteWrap();
wreq.handle = handle;
wreq.oncomplete = () => {
handle.shutdown(sreq);
testInitialized(sreq, 'ShutdownWrap');
};
const err = handle.writeLatin1String(wreq, 'hi'.repeat(100000));
if (err)
throw new Error(`write failed: ${getSystemErrorName(err)}`);
if (!stream_wrap.streamBaseState[stream_wrap.kLastWriteWasAsync]) {
testUninitialized(wreq, 'WriteWrap');
// Synchronous finish. Write more data until we hit an
// asynchronous write.
return writeData();
}
testInitialized(wreq, 'WriteWrap');
}
req.address = common.localhostIPv4;
req.port = server.address().port;
const err = handle.connect(req, req.address, req.port);
assert.strictEqual(err, 0);
testInitialized(req, 'TCPConnectWrap');
}));
}
if (common.hasCrypto) { // eslint-disable-line node-core/crypto-check
const { TCP, constants: TCPConstants } = internalBinding('tcp_wrap');
const tcp = new TCP(TCPConstants.SOCKET);
const ca = fixtures.readKey('rsa_ca.crt');
const cert = fixtures.readKey('rsa_cert.crt');
const key = fixtures.readKey('rsa_private.pem');
const credentials = require('tls').createSecureContext({ ca, cert, key });
// TLSWrap is exposed, but needs to be instantiated via tls_wrap.wrap().
const tls_wrap = internalBinding('tls_wrap');
testInitialized(tls_wrap.wrap(tcp, credentials.context, true), 'TLSWrap');
}
{
const binding = internalBinding('udp_wrap');
const handle = new binding.UDP();
const req = new binding.SendWrap();
testInitialized(handle, 'UDP');
testUninitialized(req, 'SendWrap');
handle.bind('0.0.0.0', 0, undefined);
const addr = {};
handle.getsockname(addr);
req.address = '127.0.0.1';
req.port = addr.port;
req.oncomplete = () => handle.close();
handle.send(req, [Buffer.alloc(1)], 1, req.port, req.address, true);
testInitialized(req, 'SendWrap');
}
if (process.features.inspector && common.isMainThread) {
const binding = internalBinding('inspector');
const handle = new binding.Connection(() => {});
testInitialized(handle, 'Connection');
handle.disconnect();
}
// PROVIDER_HEAPDUMP
{
v8.getHeapSnapshot().destroy();
}