/**
   * * 解析从服务器收到的包,切割完整包,并返回半包
   *
   * @param unHandledPkg 从socket收到的数据包
   * @param parser 不同的协议,传递不同的包内容解析器
   * @return 如果收到的包是一个或者多个完整包,那么返回0长度字节数组 如果收到的包是1个半或者N个半数据包,那么截取一个或N个完整包,并把剩余的部分返回
   */
  private final byte[] handlePackage(byte[] unHandledPkg, IPackageContenParser parser) {
    /** 调用一次read,从Server收到的数据包(可能是半包、1个包、1.x、2.x....) */
    int pkgLen = unHandledPkg.length;

    /** 一个完整数据包的长度 */
    int completePkgLen = parser.getCompletePackageLength(unHandledPkg);

    if (completePkgLen > pkgLen) {
      /** 当前收到的数据不到一个完整包,则直接返回,等待下一个包 */
      return unHandledPkg;
    } else if (completePkgLen == pkgLen) {
      /** 一个完整包,则直接丢到已处理队列 */
      handledQueue.offer(unHandledPkg);

      return EmptyContainer.EMPTY_BYTE_ARRAY;
    } else {
      /** 有多个包,那么就递归解析, */
      byte[] onePkg = parser.getCompletePackage(unHandledPkg);
      handledQueue.offer(onePkg);

      /** 截取除完整包后的剩余部分 */
      byte[] remain = PackageTools.getSubBytes(unHandledPkg, onePkg.length, pkgLen - onePkg.length);

      return handlePackage(remain, parser);
    }
  }
コード例 #2
0
    void offer(Action action) {
      if (DEBUG) {
        assertOnIcThread();
        Log.d(
            LOGTAG, "offer: Action(" + getConstantName(Action.class, "TYPE_", action.mType) + ")");
      }
      /* Events don't need update because they generate text/selection
      notifications which will do the updating for us */
      if (action.mType != Action.TYPE_EVENT
          && action.mType != Action.TYPE_ACKNOWLEDGE_FOCUS
          && action.mType != Action.TYPE_SET_HANDLER) {
        action.mShouldUpdate = mUpdateGecko;
      }
      if (mActions.isEmpty()) {
        mActionsActive.acquireUninterruptibly();
        mActions.offer(action);
      } else
        synchronized (this) {
          // tryAcquire here in case Gecko thread has just released it
          mActionsActive.tryAcquire();
          mActions.offer(action);
        }

      switch (action.mType) {
        case Action.TYPE_EVENT:
        case Action.TYPE_SET_SELECTION:
        case Action.TYPE_SET_SPAN:
        case Action.TYPE_REMOVE_SPAN:
        case Action.TYPE_SET_HANDLER:
          onImeSynchronize();
          break;

        case Action.TYPE_REPLACE_TEXT:
          // try key events first
          sendCharKeyEvents(action);

          // fall-through

        case Action.TYPE_COMPOSE_TEXT:
          onImeReplaceText(
              action.mStart,
              action.mEnd,
              action.mSequence.toString(),
              action.mType == Action.TYPE_COMPOSE_TEXT);
          break;

        case Action.TYPE_ACKNOWLEDGE_FOCUS:
          onImeAcknowledgeFocus();
          break;

        default:
          throw new IllegalStateException("Action not processed");
      }

      ++mIcUpdateSeqno;
    }
コード例 #3
0
  @Override
  public void release(char[] value) {

    releaseCalls.incrementAndGet();

    QUEUE.offer(value);
  }
コード例 #4
0
  public static void release(SessionRemote sr) {
    if (sr == null || sr.isClosed()) return;

    ConcurrentLinkedQueue<SessionRemote> queue = getQueue(sr.getURL());
    if (queue.size() > corePoolSize) sr.close();
    else queue.offer(sr);
  }
コード例 #5
0
  /**
   * 下载一个任务
   *
   * @param url
   * @param listener
   */
  public void addTask(String url, DownloadListener listener) {
    if (StringUtils.isEmpty(url)) {
      Logger.d("download url null");
      return;
    }

    DownloadInfo downloadInfo = new DownloadInfo();
    downloadInfo.setUrl(url);
    if (!hasTask(url)) {
      downloadInfo.setTargetFolder(mTargetFolder);
      try {
        mDbHelper.save(downloadInfo);
      } catch (DbException e) {
        Logger.e(e);
      }

      if (mDownloadingTasks.size() < MAX_DOWNLOAD_COUNT) {
        downloadInfo.setState(DownloadInfo.DOWNLOADING);
        mDownloadingTasks.add(downloadInfo);
        DownloadHttpTask task =
            new DownloadHttpTask(downloadInfo, mDownloadUIHandler, mDbHelper, this);
        mDownloadingTaskMap.put(url, task);
        task.start();
      } else { // 加入等待队列
        downloadInfo.setState(DownloadInfo.WAIT);
        boolean b = mWaitTasks.offer(downloadInfo);
        if (b) {
          addTaskListener(url, listener);
        }
      }
      mAllTasks.add(downloadInfo);
    } else {
      Logger.d("任务已存在");
    }
  }
コード例 #6
0
 /**
  * 重新下载
  *
  * @param url
  */
 public void restartTask(String url) {
   Iterator<DownloadInfo> pauseIt = mPausingTasks.iterator();
   if (mPausingTasks.size() > 0) {
     synchronized (mIteratorLock) {
       while (pauseIt.hasNext()) {
         DownloadInfo downloadInfo = pauseIt.next();
         if (TextUtils.equals(downloadInfo.getUrl(), url)) {
           if (mDownloadingTasks.size() < MAX_DOWNLOAD_COUNT) {
             downloadInfo.setState(DownloadInfo.DOWNLOADING);
             DownloadHttpTask task =
                 new DownloadHttpTask(downloadInfo, mDownloadUIHandler, mDbHelper, this);
             mDownloadingTaskMap.put(url, task);
             mDownloadingTasks.add(downloadInfo);
             pauseIt.remove();
             task.start();
           } else {
             downloadInfo.setState(DownloadInfo.WAIT);
             mWaitTasks.offer(downloadInfo);
             pauseIt.remove();
           }
           return;
         }
       }
     }
   } else {
     stopTask(url);
     restartTask(url);
   }
 }
コード例 #7
0
ファイル: PlayHandler.java プロジェクト: robfig/play
 public void writeChunk(Object chunk) throws Exception {
   String message = chunk == null ? "" : chunk.toString();
   StringWriter writer = new StringWriter();
   Integer l = message.getBytes("utf-8").length + 2;
   writer.append(Integer.toHexString(l)).append("\r\n").append(message).append("\r\n\r\n");
   nextChunks.offer(writer.toString());
 }
コード例 #8
0
ファイル: ObjectPool.java プロジェクト: jmecn/learnJME3
 public ObjectPool(Class<T> c, int preAllocate) {
   queue = new ConcurrentLinkedQueue<T>();
   this.c = c;
   for (int i = 0; i < preAllocate; i++) {
     queue.offer(newInstance());
   }
 }
コード例 #9
0
ファイル: ObjectPool.java プロジェクト: jmecn/learnJME3
 public ObjectPool(ObjectGenerator<T> generator, int preAllocate) {
   queue = new ConcurrentLinkedQueue<T>();
   this.generator = generator;
   for (int i = 0; i < preAllocate; i++) {
     queue.offer(newInstance());
   }
 }
コード例 #10
0
ファイル: FlushedQueue.java プロジェクト: rbonghi/RBFramework
 @Override
 public boolean offer(E e) {
   if (queue.size() > capacity) {
     queue.poll();
   }
   queue.offer(e);
   return true;
 }
コード例 #11
0
 public void addOperation(final MemcachedNode node, final Operation o) {
   o.initialize();
   node.addOp(o);
   addedQueue.offer(node);
   Selector s = selector.wakeup();
   assert s == selector : "Wakeup returned the wrong selector.";
   getLogger().debug("Added %s to %s", o, node);
 }
コード例 #12
0
ファイル: SVGTileProvider.java プロジェクト: kyungkoo/iosched
 public void restore(TileGenerator tileGenerator) {
   if (mPool.size() < mMaxSize && mPool.offer(tileGenerator)) {
     return;
   }
   // pool is too big or returning to pool failed, so just try to clean
   // up.
   tileGenerator.cleanUp();
 }
コード例 #13
0
 public void finishDialog(String aReturn) {
   mInputs = null;
   mButtons = null;
   mDialog = null;
   mSelected = null;
   mPromptQueue.offer(aReturn);
   // poke the Gecko thread in case it's waiting for new events
   GeckoAppShell.sendEventToGecko(GeckoEvent.createNoOpEvent());
 }
コード例 #14
0
  @TaskAction
  public void doTask()
      throws ParserConfigurationException, SAXException, IOException, InterruptedException {
    File out = new File(getAssetsDir(), "objects");
    out.mkdirs();

    AssetIndex index = getIndex();

    for (Entry<String, AssetEntry> e : index.objects.entrySet()) {
      Asset asset = new Asset(e.getValue().hash, e.getValue().size);
      File file = new File(out, asset.path);

      // exists but not the right size?? delete
      if (file.exists() && file.length() != asset.size) file.delete();

      // does the file exist (still) ??
      if (!file.exists()) filesLeft.offer(asset);
    }

    getLogger().info("Finished parsing JSON");
    int max = filesLeft.size();
    getLogger().info("Files Missing: " + max + "/" + index.objects.size());

    // get number of threads
    int threadNum = max / 100;
    if (threadNum == 0 && max > 0) threadNum++; // atleats 1 thread

    // spawn threads
    for (int i = 0; i < threadNum; i++) spawnThread();

    getLogger().info("Threads initially spawned: " + threadNum);

    while (stillRunning()) {
      int done = max - filesLeft.size();
      getLogger()
          .lifecycle(
              "Current status: "
                  + done
                  + "/"
                  + max
                  + "   "
                  + (int) ((double) done / max * 100)
                  + "%");
      spawnThread();
      Thread.sleep(1000);
    }

    if (errored) {
      // CRASH!
      getLogger().error("Something went wrong with the assets downloading!");
      this.setDidWork(false);
      return;
    }
  }
コード例 #15
0
ファイル: ZulipApp.java プロジェクト: tbutter/zulip-android
  public void markMessageAsRead(Message message) {
    if (unreadMessageHandler == null) {
      Log.e("zulipApp", "markMessageAsRead called before unreadMessageHandler was instantiated");
      return;
    }

    unreadMessageQueue.offer(message.getID());
    if (!unreadMessageHandler.hasMessages(0)) {
      unreadMessageHandler.sendEmptyMessageDelayed(0, 2000);
    }
  }
コード例 #16
0
 /** Broadcast an operation to all nodes. */
 public CountDownLatch broadcastOperation(final BroadcastOpFactory of) {
   final CountDownLatch latch = new CountDownLatch(locator.getAll().size());
   for (MemcachedNode node : locator.getAll()) {
     Operation op = of.newOp(node, latch);
     op.initialize();
     node.addOp(op);
     addedQueue.offer(node);
   }
   Selector s = selector.wakeup();
   assert s == selector : "Wakeup returned the wrong selector.";
   return latch;
 }
コード例 #17
0
ファイル: JGroupsFilter.java プロジェクト: digvan/atmosphere
 /** {@inheritDoc} */
 @Override
 public void receive(final Message message) {
   final String msg = (String) message.getObject();
   if (message.getSrc() != jchannel.getLocalAddress()) {
     if (msg != null) {
       receivedMessages.offer(msg);
       if (bc != null) {
         bc.broadcast(msg);
       }
     }
   }
 }
コード例 #18
0
  public void addOperations(final Map<MemcachedNode, Operation> ops) {

    for (Map.Entry<MemcachedNode, Operation> me : ops.entrySet()) {
      final MemcachedNode node = me.getKey();
      Operation o = me.getValue();
      o.initialize();
      node.addOp(o);
      addedQueue.offer(node);
    }
    Selector s = selector.wakeup();
    assert s == selector : "Wakeup returned the wrong selector.";
  }
コード例 #19
0
  /** {@inheritDoc} */
  @Override
  public <T> Future<T> broadcastOnResume(T msg) {

    if (destroyed.get())
      throw new IllegalStateException("This Broadcaster has been destroyed and cannot be used");

    start();
    Object newMsg = filter(msg);
    if (newMsg == null) return null;

    BroadcasterFuture<Object> f = new BroadcasterFuture<Object>(newMsg);
    broadcastOnResume.offer(new Entry(newMsg, null, f, msg));
    return f;
  }
コード例 #20
0
  /** {@inheritDoc} */
  public <T> Future<T> delayBroadcast(final T o, long delay, TimeUnit t) {

    if (destroyed.get())
      throw new IllegalStateException("This Broadcaster has been destroyed and cannot be used");

    start();
    final Object msg = filter(o);
    if (msg == null) return null;

    final BroadcasterFuture<Object> future = new BroadcasterFuture<Object>(msg);
    final Entry e = new Entry(msg, null, future, o);
    Future<T> f;
    if (delay > 0) {
      f =
          bc.getScheduledExecutorService()
              .schedule(
                  new Callable<T>() {

                    public T call() throws Exception {
                      delayedBroadcast.remove(e);
                      if (Callable.class.isAssignableFrom(o.getClass())) {
                        try {
                          Object r = Callable.class.cast(o).call();
                          final Object msg = filter(r);
                          if (msg != null) {
                            Entry entry = new Entry(msg, null, null, r);
                            push(entry);
                          }
                          return (T) msg;
                        } catch (Exception e1) {
                          logger.error("", e);
                        }
                      }

                      final Object msg = filter(o);
                      final Entry e = new Entry(msg, null, null, o);
                      push(e);
                      return (T) msg;
                    }
                  },
                  delay,
                  t);

      e.future = new BroadcasterFuture<Object>(f, msg);
    }
    delayedBroadcast.offer(e);
    return future;
  }
コード例 #21
0
 /**
  * return the instance to the manager after use
  *
  * @param buf
  */
 public void release(T buf) {
   if (_releaseQueueSize.get() > 8000) {
     log.info("release queue full");
     synchronized (MemoryManager.this) {
       MemoryManager.this.notifyAll();
     }
     return;
   }
   if (buf != null) {
     _releaseQueue.offer(buf);
     _releaseQueueSize.incrementAndGet();
     synchronized (MemoryManager.this) {
       MemoryManager.this.notifyAll();
     }
   }
 }
コード例 #22
0
    public boolean process(Socket socket) {
      Http11Processor processor = recycledProcessors.poll();
      try {

        if (processor == null) {
          processor = createProcessor();
        }

        if (processor instanceof ActionHook) {
          ((ActionHook) processor).action(ActionCode.ACTION_START, null);
        }

        if (proto.isSSLEnabled() && (proto.sslImplementation != null)) {
          processor.setSSLSupport(proto.sslImplementation.getSSLSupport(socket));
        } else {
          processor.setSSLSupport(null);
        }

        processor.process(socket);
        return false;

      } catch (java.net.SocketException e) {
        // SocketExceptions are normal
        Http11Protocol.log.debug(sm.getString("http11protocol.proto.socketexception.debug"), e);
      } catch (java.io.IOException e) {
        // IOExceptions are normal
        Http11Protocol.log.debug(sm.getString("http11protocol.proto.ioexception.debug"), e);
      }
      // Future developers: if you discover any other
      // rare-but-nonfatal exceptions, catch them here, and log as
      // above.
      catch (Throwable e) {
        // any other exception or error is odd. Here we log it
        // with "ERROR" level, so it will show up even on
        // less-than-verbose logs.
        Http11Protocol.log.error(sm.getString("http11protocol.proto.error"), e);
      } finally {
        //       if(proto.adapter != null) proto.adapter.recycle();
        //                processor.recycle();

        if (processor instanceof ActionHook) {
          ((ActionHook) processor).action(ActionCode.ACTION_STOP, null);
        }
        recycledProcessors.offer(processor);
      }
      return false;
    }
コード例 #23
0
ファイル: User.java プロジェクト: Rongmasihai/risecoin
  synchronized void send(JSONStreamAware response) {
    if (asyncContext == null) {

      if (isInactive) {
        // user not seen recently, no responses should be collected
        return;
      }
      if (pendingResponses.size() > 1000) {
        pendingResponses.clear();
        // stop collecting responses for this user
        isInactive = true;
        if (secretPhrase == null) {
          // but only completely remove users that don't have unlocked accounts
          Users.remove(this);
        }
        return;
      }

      pendingResponses.offer(response);

    } else {

      JSONArray responses = new JSONArray();
      JSONStreamAware pendingResponse;
      while ((pendingResponse = pendingResponses.poll()) != null) {

        responses.add(pendingResponse);
      }
      responses.add(response);

      JSONObject combinedResponse = new JSONObject();
      combinedResponse.put("responses", responses);

      asyncContext.getResponse().setContentType("text/plain; charset=UTF-8");

      try (Writer writer = asyncContext.getResponse().getWriter()) {
        combinedResponse.writeJSONString(writer);
      } catch (IOException e) {
        Logger.logMessage("Error sending response to user", e);
      }

      asyncContext.complete();
      asyncContext = null;
    }
  }
コード例 #24
0
  /** Send any pending topics. */
  private void sendPending() {
    while (outstanding.get() < concurrency) {
      final TopicQueue queue = pendingTopics.poll();
      if (queue == null) {
        return;
      }

      queue.pending = false;
      final int sent = queue.sendBatch();

      // Did we send a whole batch? Then there might be more messages in the queue. Mark as pending
      // again.
      if (sent == batchSize) {
        queue.pending = true;
        pendingTopics.offer(queue);
      }
    }
  }
コード例 #25
0
  private void processCameraData(
      final byte[] cameraData, final int imageWidth, final int imageHeight) {

    cameraPreviewCallbackQueue.clear();
    boolean offerSuccess =
        cameraPreviewCallbackQueue.offer(
            new Runnable() {
              @Override
              public void run() {
                long start = System.nanoTime();

                // Convert data to JPEG and compress
                YuvImage image =
                    new YuvImage(cameraData, ImageFormat.NV21, imageWidth, imageHeight, null);
                ByteArrayOutputStream out = new ByteArrayOutputStream();
                Rect area = new Rect(0, 0, imageWidth, imageHeight);
                image.compressToJpeg(area, IMAGE_QUALITY, out);
                byte[] jpegVideoFrameData = out.toByteArray();

                // rotate image
                byte[] rotatedCameraData =
                    rotateImage(jpegVideoFrameData, imageWidth, imageHeight, currentCameraId);
                if (rotatedCameraData.length == 0) {
                  return;
                }

                // send data to the opponent
                //
                cameraDataListener.onCameraDataReceive(rotatedCameraData);
                //
                //

                // close stream
                try {
                  out.close();
                } catch (IOException e) {
                  e.printStackTrace();
                }
              }
            });
  }
コード例 #26
0
 // Handle IO for a specific selector.  Any IOException will cause a
 // reconnect
 private void handleIO(SelectionKey sk) {
   MemcachedNode qa = (MemcachedNode) sk.attachment();
   try {
     getLogger()
         .debug(
             "Handling IO for:  %s (r=%s, w=%s, c=%s, op=%s)",
             sk, sk.isReadable(), sk.isWritable(), sk.isConnectable(), sk.attachment());
     if (sk.isConnectable()) {
       getLogger().info("Connection state changed for %s", sk);
       final SocketChannel channel = qa.getChannel();
       if (channel.finishConnect()) {
         assert channel.isConnected() : "Not connected.";
         qa.connected();
         addedQueue.offer(qa);
         if (qa.getWbuf().hasRemaining()) {
           handleWrites(sk, qa);
         }
       } else {
         assert !channel.isConnected() : "connected";
       }
     } else {
       if (sk.isReadable()) {
         handleReads(sk, qa);
       }
       if (sk.isWritable()) {
         handleWrites(sk, qa);
       }
     }
   } catch (Exception e) {
     // Various errors occur on Linux that wind up here.  However, any
     // particular error processing an item should simply cause us to
     // reconnect to the server.
     getLogger().info("Reconnecting due to exception on %s", qa, e);
     queueReconnect(qa);
   }
   qa.fixupOps();
 }
コード例 #27
0
  public static int processNotification(String queueArn, String remoteAddress) {

    int messageCount = 0;

    long ts1 = System.currentTimeMillis();
    CMBControllerServlet.valueAccumulator.initializeAllCounters();

    contextQueues.putIfAbsent(queueArn, new ConcurrentLinkedQueue<AsyncContext>());
    ConcurrentLinkedQueue<AsyncContext> contextQueue = contextQueues.get(queueArn);

    AsyncContext asyncContext = contextQueue.poll();

    if (asyncContext == null) {
      logger.debug(
          "event=no_pending_receive queue_arn=" + queueArn + " remote_address=" + remoteAddress);
      return messageCount;
    }

    if (asyncContext.getRequest() == null) {
      logger.info(
          "event=skipping_invalid_context queue_arn="
              + queueArn
              + " remote_address="
              + remoteAddress);
      return messageCount;
    }

    if (!(asyncContext.getRequest() instanceof CQSHttpServletRequest)) {
      logger.info(
          "event=skipping_invalid_request queue_arn="
              + queueArn
              + " remote_address="
              + remoteAddress);
      return messageCount;
    }

    CQSHttpServletRequest request = (CQSHttpServletRequest) asyncContext.getRequest();

    // skip if request is already finished or outdated

    if (!request.isActive()
        || System.currentTimeMillis() - request.getRequestReceivedTimestamp()
            > request.getWaitTime()) {
      logger.info(
          "event=skipping_outdated_context queue_arn="
              + queueArn
              + " remote_address="
              + remoteAddress);
      return messageCount;
    }

    logger.debug(
        "event=notification_received queue_arn=" + queueArn + " remote_address=" + remoteAddress);

    try {

      CQSQueue queue = request.getQueue();
      List<CQSMessage> messageList =
          PersistenceFactory.getCQSMessagePersistence()
              .receiveMessage(queue, request.getReceiveAttributes());

      if (messageList.size() > 0) {

        messageCount = messageList.size();

        List<String> receiptHandles = new ArrayList<String>();

        for (CQSMessage message : messageList) {
          receiptHandles.add(message.getReceiptHandle());
        }

        request.setReceiptHandles(receiptHandles);
        request.setAttribute("lp", "yy"); // found lp call with messages
        CQSMonitor.getInstance()
            .addNumberOfMessagesReturned(queue.getRelativeUrl(), messageList.size());
        String out =
            CQSMessagePopulator.getReceiveMessageResponseAfterSerializing(
                messageList, request.getFilterAttributes(), request.getFilterMessageAttributes());
        Action.writeResponse(out, (HttpServletResponse) asyncContext.getResponse());
        long lp_ms = System.currentTimeMillis() - ts1;
        request.setAttribute("lp_ms", lp_ms);
        String cass_msString =
            String.valueOf(
                CQSControllerServlet.valueAccumulator.getCounter(AccumulatorName.CassandraTime));
        request.setAttribute("cass_ms", cass_msString);
        request.setAttribute(
            "cass_num_rd",
            CQSControllerServlet.valueAccumulator.getCounter(AccumulatorName.CassandraRead));
        request.setAttribute(
            "cass_num_wr",
            CQSControllerServlet.valueAccumulator.getCounter(AccumulatorName.CassandraWrite));
        request.setAttribute(
            "redis_ms",
            CQSControllerServlet.valueAccumulator.getCounter(AccumulatorName.RedisTime));
        request.setAttribute(
            "io_ms", CQSControllerServlet.valueAccumulator.getCounter(AccumulatorName.IOTime));

        asyncContext.complete();

      } else {

        // if there's longpoll time left, put back on queue

        if (request.getWaitTime()
                - System.currentTimeMillis()
                + request.getRequestReceivedTimestamp()
            > 0) {
          logger.info(
              "event=no_messages_found_for_longpoll_receive action=re_queueing time_left_ms="
                  + (request.getWaitTime()
                      - System.currentTimeMillis()
                      + request.getRequestReceivedTimestamp())
                  + " queue_arn="
                  + queueArn
                  + " remote_address="
                  + remoteAddress);
          contextQueue.offer(asyncContext);
        }
      }

    } catch (Exception ex) {
      logger.error("event=longpoll_queue_error queue_arn=" + queueArn, ex);
    } finally {
      CMBControllerServlet.valueAccumulator.deleteAllCounters();
    }

    return messageCount;
  }
コード例 #28
0
 /** 生产 */
 public static void offer() {
   for (int i = 0; i < 100000; i++) {
     queue.offer(i);
   }
 }
コード例 #29
0
ファイル: User.java プロジェクト: Rongmasihai/risecoin
 void enqueue(JSONStreamAware response) {
   pendingResponses.offer(response);
 }
コード例 #30
0
ファイル: Port.java プロジェクト: k33g/golo-lang
 /**
  * Sends a message to the target worker function. This method returns immediately as message
  * processing is asynchronous.
  *
  * @param message the message of any type.
  * @return the same port object.
  */
 public Port send(Object message) {
   queue.offer(message);
   scheduleNext();
   return this;
 }