Add nanomsg to third_party, with some demos.

This isn't something I want to make part of Skia, but just a substrate to build cross-process demos on top of.  If I client were to use Skia cross-process, they'd drop their own IPC system in here.

If you're not familiar, nanomsg (nanomsg.org) is the next-gen zeromq (zeromq.org), from the same author, righting all his design wrongs from zeromq.

It's a lot like the lower half of mojo, dealing with making the connections and getting messages reliably from A to B.  Think, better sockets, and it spans nicely across in-process (with zero-copy), inter-process, and TCP.

BUG=skia:
R=bsalomon@google.com, mtklein@google.com

Author: mtklein@chromium.org

Review URL: https://codereview.chromium.org/294873004
diff --git a/experimental/nanomsg/picture_demo.cpp b/experimental/nanomsg/picture_demo.cpp
new file mode 100644
index 0000000..5a3ebdc
--- /dev/null
+++ b/experimental/nanomsg/picture_demo.cpp
@@ -0,0 +1,207 @@
+#include "nanomsg/src/nn.h"
+#include "nanomsg/src/pipeline.h"
+#include "nanomsg/src/reqrep.h"
+
+#include "SkCanvas.h"
+#include "SkCommandLineFlags.h"
+#include "SkData.h"
+#include "SkForceLinking.h"
+#include "SkGraphics.h"
+#include "SkImageEncoder.h"
+#include "SkOSFile.h"
+#include "SkPicture.h"
+#include "SkRandom.h"
+#include "SkStream.h"
+
+__SK_FORCE_IMAGE_DECODER_LINKING;
+
+// To keep things simple, PictureHeader is fixed-size POD.
+struct PictureHeader {
+    SkMatrix         matrix;
+    SkRect           clip;
+    SkXfermode::Mode xfermode;
+    pid_t            pid;
+    uint8_t          alpha;
+
+    PictureHeader()
+        : matrix(SkMatrix::I())
+        , clip(SkRect::MakeLargest())
+        , xfermode(SkXfermode::kSrcOver_Mode)
+        , pid(getpid())
+        , alpha(0xFF) {}
+};
+
+// A little adaptor: nn_iovec wants a non-const pointer for no obvious reason.
+static struct nn_iovec create_iov(const void* ptr, size_t size) {
+    struct nn_iovec iov = { const_cast<void*>(ptr), size };
+    return iov;
+}
+
+static void send_picture(int socket, const PictureHeader& header, const SkData& skp) {
+    // Vectored IO lets us send header and skp contiguously without first
+    // copying them to a contiguous buffer.
+    struct nn_iovec iov[] = {
+        create_iov(&header, sizeof(header)),
+        create_iov(skp.data(), skp.size()),
+    };
+
+    struct nn_msghdr msg;
+    sk_bzero(&msg, sizeof(msg));
+    msg.msg_iov    = iov;
+    msg.msg_iovlen = SK_ARRAY_COUNT(iov);
+
+    nn_sendmsg(socket, &msg, 0/*flags*/);
+}
+
+static SkPicture* recv_picture(int socket, PictureHeader* header) {
+    static const size_t hSize = sizeof(*header);  // It's easy to slip up and use sizeof(header).
+
+    void* msg;
+    int size = nn_recv(socket, &msg, NN_MSG, 0/*flags*/);
+    SkDebugf("%d bytes", size);
+
+    // msg is first a fixed-size header, then an .skp.
+    memcpy(header, msg, hSize);
+    SkMemoryStream stream((uint8_t*)msg + hSize, size - hSize);
+    SkPicture* pic = SkPicture::CreateFromStream(&stream);
+
+    SkDebugf(" from proccess %d:", header->pid);
+
+    nn_freemsg(msg);
+    return pic;
+}
+
+static void client(const char* skpPath, const char* dataEndpoint) {
+    // Read the .skp.
+    SkAutoTUnref<const SkData> skp(SkData::NewFromFileName(skpPath));
+    if (!skp) {
+        SkDebugf("Couldn't read %s\n", skpPath);
+        exit(1);
+    }
+    SkMemoryStream stream(skp->data(), skp->size());
+    SkAutoTUnref<SkPicture> picture(SkPicture::CreateFromStream(&stream));
+
+    PictureHeader header;
+    SkRandom rand(picture->width() * picture->height());
+    SkScalar r = rand.nextRangeScalar(0, picture->width()),
+             b = rand.nextRangeScalar(0, picture->height()),
+             l = rand.nextRangeScalar(0, r),
+             t = rand.nextRangeScalar(0, b);
+    header.clip.setLTRB(l,t,r,b);
+    header.matrix.setTranslate(-l, -t);
+    header.matrix.postRotate(rand.nextRangeScalar(-25, 25));
+    header.alpha = 0x7F;
+
+    //Clients use NN_REQ (request) type sockets.
+    int socket = nn_socket(AF_SP, NN_REQ);
+
+    // Clients connect a socket to an endpoint.
+    nn_connect(socket, dataEndpoint);
+
+    // Send the picture and its header.
+    SkDebugf("Sending %s (%d bytes)...", skpPath, skp->size());
+    send_picture(socket, header, *skp);
+
+    // Wait for ack.
+    uint8_t ack;
+    nn_recv(socket, &ack, sizeof(ack), 0/*flags*/);
+    SkDebugf(" ok.\n");
+}
+
+// Wait until socketA or socketB has something to tell us, and return which one.
+static int poll_in(int socketA, int socketB) {
+    struct nn_pollfd polls[] = {
+        { socketA, NN_POLLIN, 0 },
+        { socketB, NN_POLLIN, 0 },
+    };
+
+    nn_poll(polls, SK_ARRAY_COUNT(polls), -1/*no timeout*/);
+
+    if (polls[0].revents & NN_POLLIN) { return socketA; }
+    if (polls[1].revents & NN_POLLIN) { return socketB; }
+
+    SkFAIL("unreachable");
+    return 0;
+}
+
+static void server(const char* dataEndpoint, const char* controlEndpoint, SkCanvas* canvas) {
+    // NN_REP sockets receive a request then make a reply.  NN_PULL sockets just receive a request.
+    int data    = nn_socket(AF_SP, NN_REP);
+    int control = nn_socket(AF_SP, NN_PULL);
+
+    // Servers bind a socket to an endpoint.
+    nn_bind(data,    dataEndpoint);
+    nn_bind(control, controlEndpoint);
+
+    while (true) {
+        int ready = poll_in(data, control);
+
+        // If we got any message on the control socket, we can stop.
+        if (ready == control) {
+            break;
+        }
+
+        // We should have an .skp waiting for us on data socket.
+        PictureHeader header;
+        SkAutoTUnref<SkPicture> picture(recv_picture(data, &header));
+
+        SkPaint paint;
+        paint.setAlpha(header.alpha);
+        paint.setXfermodeMode(header.xfermode);
+
+        canvas->saveLayer(NULL, &paint);
+            canvas->concat(header.matrix);
+            canvas->clipRect(header.clip);
+            picture->draw(canvas);
+        canvas->restore();
+        SkDebugf(" drew");
+
+        // Send back an ack.
+        uint8_t ack = 42;
+        nn_send(data, &ack, sizeof(ack), 0/*flags*/);
+        SkDebugf(" and acked.\n");
+    }
+}
+
+static void stop(const char* controlEndpoint) {
+    // An NN_PUSH socket can send messages but not receive them.
+    int control = nn_socket(AF_SP, NN_PUSH);
+    nn_connect(control, controlEndpoint);
+
+    // Sending anything (including this 0-byte message) will tell server() to stop.
+    nn_send(control, NULL, 0, 0/*flags*/);
+}
+
+DEFINE_string2(skp, r, "", ".skp to send (as client)");
+DEFINE_string2(png, w, "", ".png to write (as server)");
+DEFINE_bool(stop, false, "If true, tell server to stop and write its canvas out as a .png.");
+DEFINE_string(data,    "ipc://nanomsg-picture-data",    "Endpoint for sending pictures.");
+DEFINE_string(control, "ipc://nanomsg-picture-control", "Endpoint for control channel.");
+
+int main(int argc, char** argv) {
+    SkAutoGraphics ag;
+    SkCommandLineFlags::Parse(argc, argv);
+
+    if (FLAGS_stop) {
+        stop(FLAGS_control[0]);
+    }
+
+    if (!FLAGS_skp.isEmpty()) {
+        client(FLAGS_skp[0], FLAGS_data[0]);
+    }
+
+    if (!FLAGS_png.isEmpty()) {
+        SkBitmap bitmap;
+        bitmap.allocN32Pixels(1000, 1000);
+        SkCanvas canvas(bitmap);
+        canvas.clear(0xFFFFFFFF);
+
+        server(FLAGS_data[0], FLAGS_control[0], &canvas);
+        canvas.flush();
+
+        SkImageEncoder::EncodeFile(FLAGS_png[0], bitmap, SkImageEncoder::kPNG_Type, 100);
+        SkDebugf("Wrote %s.\n", FLAGS_png[0]);
+    }
+
+    return 0;
+}
diff --git a/gyp/most.gyp b/gyp/most.gyp
index bd90949..3b1fa3d 100644
--- a/gyp/most.gyp
+++ b/gyp/most.gyp
@@ -31,6 +31,9 @@
         ['skia_os == "ios"', {
           'dependencies!': [ 'SampleApp.gyp:SampleApp' ],
         }],
+        ['skia_os == "mac" or skia_os == "linux"', {
+          'dependencies': [ 'nanomsg.gyp:*' ],
+        }],
         [ 'skia_skip_gui',
           {
             'dependencies!': [
diff --git a/gyp/nanomsg.gyp b/gyp/nanomsg.gyp
new file mode 100644
index 0000000..1a88c4a
--- /dev/null
+++ b/gyp/nanomsg.gyp
@@ -0,0 +1,204 @@
+{
+  'variables': {
+    'skia_warnings_as_errors': 0,
+  },
+  'targets': [{
+    # Draws pictures cross-process.
+    'target_name': 'nanomsg_picture_demo',
+    'type': 'executable',
+    'dependencies': [
+      'skia_lib.gyp:skia_lib',
+      'flags.gyp:flags',
+      'libnanomsg',
+    ],
+    'sources': [ '../experimental/nanomsg/picture_demo.cpp' ],
+  },{
+    'target_name': 'libnanomsg',
+    'type': 'static_library',
+
+    # Clients can include nanomsg public header foo.h with #include "nanomsg/src/foo.h"
+    'direct_dependent_settings': {
+      'include_dirs': [ '../third_party/externals' ]
+    },
+
+    # To refresh: cd gyp; find ../third_party/externals/nanomsg/src -name "*.c"
+    'sources': [
+      '../third_party/externals/nanomsg/src/aio/ctx.c',
+      '../third_party/externals/nanomsg/src/aio/fsm.c',
+      '../third_party/externals/nanomsg/src/aio/poller.c',
+      '../third_party/externals/nanomsg/src/aio/pool.c',
+      '../third_party/externals/nanomsg/src/aio/timer.c',
+      '../third_party/externals/nanomsg/src/aio/timerset.c',
+      '../third_party/externals/nanomsg/src/aio/usock.c',
+      '../third_party/externals/nanomsg/src/aio/worker.c',
+      '../third_party/externals/nanomsg/src/core/device.c',
+      '../third_party/externals/nanomsg/src/core/ep.c',
+      '../third_party/externals/nanomsg/src/core/epbase.c',
+      '../third_party/externals/nanomsg/src/core/global.c',
+      '../third_party/externals/nanomsg/src/core/pipe.c',
+      '../third_party/externals/nanomsg/src/core/poll.c',
+      '../third_party/externals/nanomsg/src/core/sock.c',
+      '../third_party/externals/nanomsg/src/core/sockbase.c',
+      '../third_party/externals/nanomsg/src/core/symbol.c',
+      '../third_party/externals/nanomsg/src/protocols/bus/bus.c',
+      '../third_party/externals/nanomsg/src/protocols/bus/xbus.c',
+      '../third_party/externals/nanomsg/src/protocols/pair/pair.c',
+      '../third_party/externals/nanomsg/src/protocols/pair/xpair.c',
+      '../third_party/externals/nanomsg/src/protocols/pipeline/pull.c',
+      '../third_party/externals/nanomsg/src/protocols/pipeline/push.c',
+      '../third_party/externals/nanomsg/src/protocols/pipeline/xpull.c',
+      '../third_party/externals/nanomsg/src/protocols/pipeline/xpush.c',
+      '../third_party/externals/nanomsg/src/protocols/pubsub/pub.c',
+      '../third_party/externals/nanomsg/src/protocols/pubsub/sub.c',
+      '../third_party/externals/nanomsg/src/protocols/pubsub/trie.c',
+      '../third_party/externals/nanomsg/src/protocols/pubsub/xpub.c',
+      '../third_party/externals/nanomsg/src/protocols/pubsub/xsub.c',
+      '../third_party/externals/nanomsg/src/protocols/reqrep/rep.c',
+      '../third_party/externals/nanomsg/src/protocols/reqrep/req.c',
+      '../third_party/externals/nanomsg/src/protocols/reqrep/xrep.c',
+      '../third_party/externals/nanomsg/src/protocols/reqrep/xreq.c',
+      '../third_party/externals/nanomsg/src/protocols/survey/respondent.c',
+      '../third_party/externals/nanomsg/src/protocols/survey/surveyor.c',
+      '../third_party/externals/nanomsg/src/protocols/survey/xrespondent.c',
+      '../third_party/externals/nanomsg/src/protocols/survey/xsurveyor.c',
+      '../third_party/externals/nanomsg/src/protocols/utils/dist.c',
+      '../third_party/externals/nanomsg/src/protocols/utils/excl.c',
+      '../third_party/externals/nanomsg/src/protocols/utils/fq.c',
+      '../third_party/externals/nanomsg/src/protocols/utils/lb.c',
+      '../third_party/externals/nanomsg/src/protocols/utils/priolist.c',
+      '../third_party/externals/nanomsg/src/transports/inproc/binproc.c',
+      '../third_party/externals/nanomsg/src/transports/inproc/cinproc.c',
+      '../third_party/externals/nanomsg/src/transports/inproc/inproc.c',
+      '../third_party/externals/nanomsg/src/transports/inproc/ins.c',
+      '../third_party/externals/nanomsg/src/transports/inproc/msgqueue.c',
+      '../third_party/externals/nanomsg/src/transports/inproc/sinproc.c',
+      '../third_party/externals/nanomsg/src/transports/ipc/aipc.c',
+      '../third_party/externals/nanomsg/src/transports/ipc/bipc.c',
+      '../third_party/externals/nanomsg/src/transports/ipc/cipc.c',
+      '../third_party/externals/nanomsg/src/transports/ipc/ipc.c',
+      '../third_party/externals/nanomsg/src/transports/ipc/sipc.c',
+      '../third_party/externals/nanomsg/src/transports/tcp/atcp.c',
+      '../third_party/externals/nanomsg/src/transports/tcp/btcp.c',
+      '../third_party/externals/nanomsg/src/transports/tcp/ctcp.c',
+      '../third_party/externals/nanomsg/src/transports/tcp/stcp.c',
+      '../third_party/externals/nanomsg/src/transports/tcp/tcp.c',
+      '../third_party/externals/nanomsg/src/transports/utils/backoff.c',
+      '../third_party/externals/nanomsg/src/transports/utils/dns.c',
+      '../third_party/externals/nanomsg/src/transports/utils/iface.c',
+      '../third_party/externals/nanomsg/src/transports/utils/literal.c',
+      '../third_party/externals/nanomsg/src/transports/utils/port.c',
+      '../third_party/externals/nanomsg/src/transports/utils/streamhdr.c',
+      '../third_party/externals/nanomsg/src/utils/alloc.c',
+      '../third_party/externals/nanomsg/src/utils/atomic.c',
+      '../third_party/externals/nanomsg/src/utils/chunk.c',
+      '../third_party/externals/nanomsg/src/utils/chunkref.c',
+      '../third_party/externals/nanomsg/src/utils/clock.c',
+      '../third_party/externals/nanomsg/src/utils/closefd.c',
+      '../third_party/externals/nanomsg/src/utils/efd.c',
+      '../third_party/externals/nanomsg/src/utils/err.c',
+      '../third_party/externals/nanomsg/src/utils/glock.c',
+      '../third_party/externals/nanomsg/src/utils/hash.c',
+      '../third_party/externals/nanomsg/src/utils/list.c',
+      '../third_party/externals/nanomsg/src/utils/msg.c',
+      '../third_party/externals/nanomsg/src/utils/mutex.c',
+      '../third_party/externals/nanomsg/src/utils/queue.c',
+      '../third_party/externals/nanomsg/src/utils/random.c',
+      '../third_party/externals/nanomsg/src/utils/sem.c',
+      '../third_party/externals/nanomsg/src/utils/sleep.c',
+      '../third_party/externals/nanomsg/src/utils/stopwatch.c',
+      '../third_party/externals/nanomsg/src/utils/thread.c',
+      '../third_party/externals/nanomsg/src/utils/wire.c',
+    ],
+
+    # TODO(mtklein): Support Windows?
+    # To refresh: cd third_party/externals/nanomsg; ./autogen.sh; ./configure; copy from Makefile.
+    'conditions': [
+      ['skia_os == "linux"', {
+        'cflags': [ '-Wno-missing-field-initializers' ],
+        'libraries': [ '-lanl' ],       # Provides getaddrinfo_a and co.
+        'direct_dependent_settings': {
+            'libraries': [ '-lanl' ],
+        },
+        'defines=': [             # equals sign throws away most Skia defines (just noise)
+          'HAVE_ACCEPT4',
+          'HAVE_ARPA_INET_H',
+          'HAVE_CLOCK_GETTIME',
+          'HAVE_DLFCN_H',
+          'HAVE_EPOLL_CREATE',
+          'HAVE_EVENTFD',
+          'HAVE_GETIFADDRS',
+          'HAVE_INTTYPES_H',
+          'HAVE_MEMORY_H',
+          'HAVE_NETDB_H',
+          'HAVE_NETINET_IN_H',
+          'HAVE_PIPE',
+          'HAVE_PIPE2',
+          'HAVE_POLL',
+          'HAVE_PTHREAD_PRIO_INHERIT',
+          'HAVE_STDINT_H',
+          'HAVE_STDLIB_H',
+          'HAVE_STRINGS_H',
+          'HAVE_STRING_H',
+          'HAVE_SYS_IOCTL_H',
+          'HAVE_SYS_SOCKET_H',
+          'HAVE_SYS_STAT_H',
+          'HAVE_SYS_TYPES_H',
+          'HAVE_UNISTD_H',
+          'HAVE_UNISTD_H',
+          'NN_HAVE_ACCEPT4',
+          'NN_HAVE_CLANG',
+          'NN_HAVE_EVENTFD',
+          'NN_HAVE_GCC',
+          'NN_HAVE_GETADDRINFO_A',
+          'NN_HAVE_LINUX',
+          'NN_HAVE_PIPE',
+          'NN_HAVE_PIPE2',
+          'NN_HAVE_POLL',
+          'NN_HAVE_SEMAPHORE',
+          'NN_HAVE_SOCKETPAIR',
+          'NN_USE_EPOLL',
+          'NN_USE_EVENTFD',
+          'NN_USE_IFADDRS',
+          'STDC_HEADERS',
+          '_GNU_SOURCE',
+        ],
+      }],
+      ['skia_os == "mac"', {
+        'defines=': [             # equals sign throws away most Skia defines (just noise)
+          'HAVE_ARPA_INET_H',
+          'HAVE_DLFCN_H',
+          'HAVE_GETIFADDRS',
+          'HAVE_INTTYPES_H',
+          'HAVE_KQUEUE',
+          'HAVE_MEMORY_H',
+          'HAVE_NETDB_H',
+          'HAVE_NETINET_IN_H',
+          'HAVE_PIPE',
+          'HAVE_POLL',
+          'HAVE_PTHREAD_PRIO_INHERIT',
+          'HAVE_STDINT_H',
+          'HAVE_STDLIB_H',
+          'HAVE_STRINGS_H',
+          'HAVE_STRING_H',
+          'HAVE_SYS_IOCTL_H',
+          'HAVE_SYS_SOCKET_H',
+          'HAVE_SYS_STAT_H',
+          'HAVE_SYS_TYPES_H',
+          'HAVE_UNISTD_H',
+          'NN_HAVE_CLANG',
+          'NN_HAVE_GCC',
+          'NN_HAVE_OSX',
+          'NN_HAVE_PIPE',
+          'NN_HAVE_POLL',
+          'NN_HAVE_SEMAPHORE',
+          'NN_HAVE_SOCKETPAIR',
+          'NN_USE_IFADDRS',
+          'NN_USE_KQUEUE',
+          'NN_USE_PIPE',
+          'STDC_HEADERS',
+          '_THREAD_SAFE',
+        ],
+      }],
+    ]
+  }]
+}