public static Throwable throwable(@NotNull ValueIn valueIn, boolean appendCurrentStack) {
    Class type = valueIn.typePrefix();
    String preMessage = null;
    Throwable throwable = ObjectUtils.newInstance((Class<Throwable>) type);

    final String finalPreMessage = preMessage;
    final Throwable finalThrowable = throwable;
    final List<StackTraceElement> stes = new ArrayList<>();
    valueIn.marshallable(
        m -> {
          final String message = merge(finalPreMessage, m.read(() -> "message").text());

          if (message != null) {
            try {
              DETAILED_MESSAGE.set(finalThrowable, message);
            } catch (IllegalAccessException e) {
              throw Jvm.rethrow(e);
            }
          }
          m.read(() -> "stackTrace")
              .sequence(
                  stes,
                  (stes0, stackTrace) -> {
                    while (stackTrace.hasNextSequenceItem()) {
                      stackTrace.marshallable(
                          r -> {
                            final String declaringClass = r.read(() -> "class").text();
                            final String methodName = r.read(() -> "method").text();
                            final String fileName = r.read(() -> "file").text();
                            final int lineNumber = r.read(() -> "line").int32();

                            stes0.add(
                                new StackTraceElement(
                                    declaringClass, methodName, fileName, lineNumber));
                          });
                    }
                  });
        });

    if (appendCurrentStack) {
      stes.add(new StackTraceElement("~ remote", "tcp ~", "", 0));
      StackTraceElement[] stes2 = Thread.currentThread().getStackTrace();
      int first = 6;
      int last = Jvm.trimLast(first, stes2);
      //noinspection ManualArrayToCollectionCopy
      for (int i = first; i <= last; i++) stes.add(stes2[i]);
    }
    try {
      //noinspection ToArrayCallWithZeroLengthArrayArgument
      STACK_TRACE.set(finalThrowable, stes.toArray(NO_STE));
    } catch (IllegalAccessException e) {
      throw Jvm.rethrow(e);
    }
    return throwable;
  }
  @Test
  public void testWriteWhileReading() {
    ClassAliasPool.CLASS_ALIASES.addAlias(Message1.class);
    ClassAliasPool.CLASS_ALIASES.addAlias(Message2.class);

    File path1 = Utils.tempDir("testWriteWhileReading1");
    File path2 = Utils.tempDir("testWriteWhileReading2");

    try (SingleChronicleQueue queue1 =
            SingleChronicleQueueBuilder.binary(path1).testBlockSize().build();
        SingleChronicleQueue queue2 =
            SingleChronicleQueueBuilder.binary(path2).testBlockSize().build()) {
      MethodReader reader2 =
          queue1.createTailer().methodReader(ObjectUtils.printAll(MessageListener.class));
      MessageListener writer2 = queue2.acquireAppender().methodWriter(MessageListener.class);
      MessageListener processor = new MessageProcessor(writer2);
      MethodReader reader1 = queue1.createTailer().methodReader(processor);
      MessageListener writer1 = queue1.acquireAppender().methodWriter(MessageListener.class);

      for (int i = 0; i < 3; i++) {
        // write a message
        writer1.method1(new Message1("hello"));
        writer1.method2(new Message2(234));

        // read those messages
        assertTrue(reader1.readOne());
        assertTrue(reader1.readOne());
        assertFalse(reader1.readOne());

        // read the produced messages
        assertTrue(reader2.readOne());
        assertTrue(reader2.readOne());
        assertFalse(reader2.readOne());
      }
      //            System.out.println(queue1.dump());
    }
  }