├── tests ├── tests.iml ├── logging.config └── src │ └── org │ └── jsl │ └── tests │ ├── queue_socket_send │ ├── SessionFactory.java │ ├── Session.java │ ├── Main.java │ ├── Receiver.java │ └── Sender.java │ ├── msg_size_eq_block_size │ ├── Main.java │ ├── Client.java │ └── Server.java │ ├── session_close │ └── Main.java │ ├── Util.java │ ├── shmem_throughput │ └── Main.java │ ├── thread_pool_throughput │ ├── Test.java │ ├── Main.java │ ├── ExecutorTest.java │ └── ThreadPoolTest.java │ ├── recv_throughput │ ├── Main.java │ ├── Server.java │ └── Client.java │ ├── buffer_overlap_copy │ └── Main.java │ ├── echo_latency │ ├── Main.java │ ├── Server.java │ └── Client.java │ ├── session_latency │ ├── Main.java │ ├── Client.java │ └── Server.java │ ├── echo_throughput │ ├── Main.java │ └── Server.java │ ├── send_throughput │ └── Main.java │ ├── session_throughput │ ├── Main.java │ └── Server.java │ ├── sched_latency │ ├── Main.java │ ├── SL_ThreadPool.java │ ├── SL_Semaphore.java │ ├── SL_Executor.java │ └── SL_Cond.java │ ├── unit │ └── Main.java │ ├── binary_queue │ ├── Generator.java │ └── Main.java │ ├── thread_pool │ └── Main.java │ ├── dgram_listener │ └── Sender.java │ ├── pubsub │ ├── SubClient.java │ └── PubClient.java │ ├── StreamDefragger.java │ ├── message_queue │ └── Main.java │ └── timer_queue │ └── Main.java ├── LICENSE.txt ├── ant_tasks └── GetTestNameTask.java ├── src └── main │ └── java │ └── org │ └── jsl │ └── collider │ ├── DataBlock.java │ ├── DatagramListener.java │ ├── Connector.java │ ├── ShMemServer.java │ ├── RetainableDataBlock.java │ ├── RetainableByteBufferCache.java │ ├── LogFormatter.java │ ├── Acceptor.java │ ├── SessionEmitter.java │ ├── StatCounter.java │ ├── PerfCounter.java │ ├── ObjectCache.java │ ├── Util.java │ ├── RetainableByteBufferImpl.java │ ├── ShMemClient.java │ ├── Session.java │ ├── SessionEmitterImpl.java │ └── Collider.java ├── pom.xml └── README.md /tests/tests.iml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | -------------------------------------------------------------------------------- /tests/logging.config: -------------------------------------------------------------------------------- 1 | #-Djava.util.logging.config.file=tests/logging.config 2 | #handlers = java.util.logging.FileHandler 3 | #handlers = java.util.logging.ConsoleHandler 4 | 5 | java.util.logging.FileHandler.pattern = tests/log%u.log 6 | java.util.logging.FileHandler.formatter = org.jsl.collider.LogFormatter 7 | 8 | java.util.logging.ConsoleHandler.formatter = org.jsl.collider.LogFormatter 9 | java.util.logging.ConsoleHandler.level = ALL 10 | 11 | org.jsl.collider.Collider.level = FINE 12 | org.jsl.collider.Acceptor.level = FINE 13 | org.jsl.collider.Connector.level = FINE 14 | org.jsl.collider.Session.level = FINE 15 | org.jsl.collider.Datagram.level = FINE 16 | -------------------------------------------------------------------------------- /LICENSE.txt: -------------------------------------------------------------------------------- 1 | 2 | 3 | JS-Collider library is dual-licensed: you can redistribute it and/or 4 | modify it under the terms of the GNU Affero General Public License as 5 | published by the Free Software Foundation, either version 3 of the 6 | License, or (at your option) any later version. 7 | 8 | This program is distributed in the hope that it will be useful, 9 | but WITHOUT ANY WARRANTY; without even the implied warranty of 10 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 11 | GNU Affero General Public License for more details. 12 | 13 | You should have received a copy of the GNU Affero General Public License 14 | along with this program (see LICENSE.agpl file) 15 | If not, see . 16 | 17 | 18 | Alternatively, you can license this library under 19 | a different type of license (not necessary commercial). 20 | Contact info@js-labs.org for further information. 21 | -------------------------------------------------------------------------------- /ant_tasks/GetTestNameTask.java: -------------------------------------------------------------------------------- 1 | import java.util.Map; 2 | import org.apache.tools.ant.BuildException; 3 | import org.apache.tools.ant.Project; 4 | import org.apache.tools.ant.Task; 5 | 6 | public class GetTestNameTask extends Task { 7 | private String m_propertyName; 8 | 9 | public void setPropertyName(String propertyName) { 10 | m_propertyName = propertyName; 11 | } 12 | 13 | public void execute() throws BuildException { 14 | final String owningTargetName = getOwningTarget().getName(); 15 | final String prefix = "test."; 16 | if (!owningTargetName.startsWith(prefix)) { 17 | throw new BuildException("target name must has prefix '" + prefix + "'"); 18 | } 19 | if (m_propertyName == null) { 20 | throw new BuildException("missing 'propertyName' attribite value"); 21 | } 22 | final Project project = getProject(); 23 | project.setProperty(m_propertyName, owningTargetName.substring(prefix.length())); 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /tests/src/org/jsl/tests/queue_socket_send/SessionFactory.java: -------------------------------------------------------------------------------- 1 | /* 2 | * JS-Collider framework tests. 3 | * Copyright (C) 2013 Sergey Zubarev 4 | * info@js-labs.org 5 | * 6 | * This program is free software: you can redistribute it and/or modify 7 | * it under the terms of the GNU Affero General Public License as 8 | * published by the Free Software Foundation, either version 3 of the 9 | * License, or (at your option) any later version. 10 | * 11 | * This program is distributed in the hope that it will be useful, 12 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 13 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 | * GNU Affero General Public License for more details. 15 | * 16 | * You should have received a copy of the GNU Affero General Public License 17 | * along with this program. If not, see . 18 | */ 19 | 20 | package org.jsl.tests.queue_socket_send; 21 | 22 | import java.nio.channels.SocketChannel; 23 | 24 | public interface SessionFactory 25 | { 26 | public Session createSession( SocketChannel socketChannel ); 27 | } 28 | -------------------------------------------------------------------------------- /tests/src/org/jsl/tests/msg_size_eq_block_size/Main.java: -------------------------------------------------------------------------------- 1 | /* 2 | * JS-Collider framework tests. 3 | * Copyright (C) 2018 Sergey Zubarev 4 | * info@js-labs.org 5 | * 6 | * This program is free software: you can redistribute it and/or modify 7 | * it under the terms of the GNU Affero General Public License as 8 | * published by the Free Software Foundation, either version 3 of the 9 | * License, or (at your option) any later version. 10 | * 11 | * This program is distributed in the hope that it will be useful, 12 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 13 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 | * GNU Affero General Public License for more details. 15 | * 16 | * You should have received a copy of the GNU Affero General Public License 17 | * along with this program. If not, see . 18 | */ 19 | 20 | package org.jsl.tests.msg_size_eq_block_size; 21 | 22 | public class Main 23 | { 24 | public static void main(String [] args) 25 | { 26 | int messages = 10; 27 | int messageLength = 512; 28 | new Server(messages, messageLength).run(); 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /tests/src/org/jsl/tests/session_close/Main.java: -------------------------------------------------------------------------------- 1 | /* 2 | * JS-Collider framework tests. 3 | * Copyright (C) 2013 Sergey Zubarev 4 | * info@js-labs.org 5 | * 6 | * This program is free software: you can redistribute it and/or modify 7 | * it under the terms of the GNU Affero General Public License as 8 | * published by the Free Software Foundation, either version 3 of the 9 | * License, or (at your option) any later version. 10 | * 11 | * This program is distributed in the hope that it will be useful, 12 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 13 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 | * GNU Affero General Public License for more details. 15 | * 16 | * You should have received a copy of the GNU Affero General Public License 17 | * along with this program. If not, see . 18 | */ 19 | 20 | package org.jsl.tests.session_close; 21 | 22 | public class Main 23 | { 24 | public static void main( String [] args ) 25 | { 26 | int socketBufferSize = 64*1024; 27 | System.out.println( "Session close test: " ); 28 | new Server(new Client()).run( socketBufferSize ); 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /tests/src/org/jsl/tests/Util.java: -------------------------------------------------------------------------------- 1 | /* 2 | * JS-Collider framework. 3 | * Copyright (C) 2013 Sergey Zubarev 4 | * info@js-labs.org 5 | * 6 | * This program is free software: you can redistribute it and/or modify 7 | * it under the terms of the GNU General Public License as published by 8 | * the Free Software Foundation, either version 3 of the License, or 9 | * (at your option) any later version. 10 | * This program is distributed in the hope that it will be useful, 11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. 13 | * See the GNU General Public License for more details. 14 | * You should have received a copy of the GNU General Public License 15 | * along with this program. If not, see . 16 | */ 17 | 18 | package org.jsl.tests; 19 | 20 | public class Util 21 | { 22 | public static String formatDelay( long startTime, long endTime ) 23 | { 24 | final long delay = ((endTime - startTime) / 1000); 25 | final long sec = (delay / 1000000); 26 | final long usec = (delay % 1000000); 27 | return String.format( "%d.%06d", sec, usec ); 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /src/main/java/org/jsl/collider/DataBlock.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2013 Sergey Zubarev, info@js-labs.org 3 | * 4 | * This file is a part of JS-Collider framework. 5 | * 6 | * This program is free software: you can redistribute it and/or modify 7 | * it under the terms of the GNU Affero General Public License as 8 | * published by the Free Software Foundation, either version 3 of the 9 | * License, or (at your option) any later version. 10 | * 11 | * This program is distributed in the hope that it will be useful, 12 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 13 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 | * GNU Affero General Public License for more details. 15 | * 16 | * You should have received a copy of the GNU Affero General Public License 17 | * along with this program. If not, see . 18 | */ 19 | 20 | package org.jsl.collider; 21 | 22 | import java.nio.ByteBuffer; 23 | 24 | public class DataBlock 25 | { 26 | public DataBlock next; 27 | public final ByteBuffer wr; 28 | public final ByteBuffer rd; 29 | 30 | public DataBlock(ByteBuffer buf) 31 | { 32 | wr = buf; 33 | rd = buf.duplicate(); 34 | rd.order(buf.order()); 35 | } 36 | 37 | public final DataBlock reset() 38 | { 39 | next = null; 40 | wr.clear(); 41 | rd.clear(); 42 | return this; 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /tests/src/org/jsl/tests/shmem_throughput/Main.java: -------------------------------------------------------------------------------- 1 | /* 2 | * JS-Collider framework. 3 | * Copyright (C) 2013 Sergey Zubarev 4 | * info@js-labs.org 5 | * 6 | * This program is free software: you can redistribute it and/or modify 7 | * it under the terms of the GNU Affero General Public License as 8 | * published by the Free Software Foundation, either version 3 of the 9 | * License, or (at your option) any later version. 10 | * 11 | * This program is distributed in the hope that it will be useful, 12 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 13 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 | * GNU Affero General Public License for more details. 15 | * 16 | * You should have received a copy of the GNU Affero General Public License 17 | * along with this program. If not, see . 18 | */ 19 | 20 | package org.jsl.tests.shmem_throughput; 21 | 22 | import org.jsl.collider.Collider; 23 | import java.io.IOException; 24 | 25 | public class Main 26 | { 27 | public static void main( String [] args ) 28 | { 29 | int sessions = 1; 30 | int messages = 100000; 31 | int messageLength = 500; 32 | 33 | try 34 | { 35 | final Collider collider = Collider.create(); 36 | collider.addAcceptor( new Server.Acceptor(sessions, messages, messageLength) ); 37 | collider.run(); 38 | } 39 | catch (IOException ex) 40 | { 41 | ex.printStackTrace(); 42 | } 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /tests/src/org/jsl/tests/thread_pool_throughput/Test.java: -------------------------------------------------------------------------------- 1 | /* 2 | * JS-Collider framework. 3 | * Copyright (C) 2013 Sergey Zubarev 4 | * info@js-labs.org 5 | * 6 | * This program is free software: you can redistribute it and/or modify 7 | * it under the terms of the GNU Affero General Public License as 8 | * published by the Free Software Foundation, either version 3 of the 9 | * License, or (at your option) any later version. 10 | * 11 | * This program is distributed in the hope that it will be useful, 12 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 13 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 | * GNU Affero General Public License for more details. 15 | * 16 | * You should have received a copy of the GNU Affero General Public License 17 | * along with this program. If not, see . 18 | */ 19 | 20 | package org.jsl.tests.thread_pool_throughput; 21 | 22 | public abstract class Test 23 | { 24 | protected final int m_totalEvents; 25 | protected final int m_producers; 26 | protected final int m_workers; 27 | 28 | protected Test( int totalEvents, int producers, int workers ) 29 | { 30 | assert( (totalEvents % producers) == 0 ); 31 | m_totalEvents = totalEvents; 32 | m_producers = producers; 33 | m_workers = workers; 34 | } 35 | 36 | public final int getProducers() { return m_producers; } 37 | public final int getWorkers() { return m_workers; } 38 | 39 | public abstract String getName(); 40 | public abstract long runTest(); 41 | } 42 | -------------------------------------------------------------------------------- /tests/src/org/jsl/tests/queue_socket_send/Session.java: -------------------------------------------------------------------------------- 1 | /* 2 | * JS-Collider framework tests. 3 | * Copyright (C) 2013 Sergey Zubarev 4 | * info@js-labs.org 5 | * 6 | * This program is free software: you can redistribute it and/or modify 7 | * it under the terms of the GNU Affero General Public License as 8 | * published by the Free Software Foundation, either version 3 of the 9 | * License, or (at your option) any later version. 10 | * 11 | * This program is distributed in the hope that it will be useful, 12 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 13 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 | * GNU Affero General Public License for more details. 15 | * 16 | * You should have received a copy of the GNU Affero General Public License 17 | * along with this program. If not, see . 18 | */ 19 | 20 | package org.jsl.tests.queue_socket_send; 21 | 22 | import java.io.IOException; 23 | import java.nio.ByteBuffer; 24 | import java.nio.channels.SocketChannel; 25 | 26 | public abstract class Session 27 | { 28 | protected final SocketChannel m_socketChannel; 29 | 30 | public Session( SocketChannel socketChannel ) 31 | { 32 | m_socketChannel = socketChannel; 33 | } 34 | 35 | public void close() 36 | { 37 | try 38 | { 39 | m_socketChannel.close(); 40 | } 41 | catch (IOException ex) 42 | { 43 | ex.printStackTrace(); 44 | } 45 | } 46 | 47 | public abstract void sendData( ByteBuffer data ); 48 | } 49 | -------------------------------------------------------------------------------- /tests/src/org/jsl/tests/recv_throughput/Main.java: -------------------------------------------------------------------------------- 1 | /* 2 | * JS-Collider framework tests. 3 | * Copyright (C) 2013 Sergey Zubarev 4 | * info@js-labs.org 5 | * 6 | * This program is free software: you can redistribute it and/or modify 7 | * it under the terms of the GNU Affero General Public License as 8 | * published by the Free Software Foundation, either version 3 of the 9 | * License, or (at your option) any later version. 10 | * 11 | * This program is distributed in the hope that it will be useful, 12 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 13 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 | * GNU Affero General Public License for more details. 15 | * 16 | * You should have received a copy of the GNU Affero General Public License 17 | * along with this program. If not, see . 18 | */ 19 | 20 | package org.jsl.tests.recv_throughput; 21 | 22 | public class Main 23 | { 24 | public static void main( String [] args ) 25 | { 26 | int sessions = 1; 27 | int messages = 1000000; 28 | int messageLength = 500; 29 | int socketBufferSize = (64*1024); 30 | 31 | if (args.length > 0) 32 | sessions = Integer.parseInt( args[0] ); 33 | if (args.length > 1) 34 | messages = Integer.parseInt( args[1] ); 35 | if (args.length > 2) 36 | messageLength = Integer.parseInt( args[2] ); 37 | 38 | Client client = new Client( sessions, messages, messageLength, socketBufferSize ); 39 | new Server(client, socketBufferSize).run(); 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /tests/src/org/jsl/tests/buffer_overlap_copy/Main.java: -------------------------------------------------------------------------------- 1 | /* 2 | * JS-Collider framework test. 3 | * Copyright (C) 2013 Sergey Zubarev 4 | * info@js-labs.org 5 | * 6 | * This program is free software: you can redistribute it and/or modify 7 | * it under the terms of the GNU General Public License as published by 8 | * the Free Software Foundation, either version 3 of the License, or 9 | * (at your option) any later version. 10 | * This program is distributed in the hope that it will be useful, 11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. 13 | * See the GNU General Public License for more details. 14 | * You should have received a copy of the GNU General Public License 15 | * along with this program. If not, see . 16 | */ 17 | 18 | package org.jsl.tests.buffer_overlap_copy; 19 | 20 | import java.nio.ByteBuffer; 21 | 22 | public class Main 23 | { 24 | final static int COUNT = 10; 25 | 26 | private static void testBuffer( final ByteBuffer bb ) throws Exception 27 | { 28 | final ByteBuffer dd = bb.duplicate(); 29 | 30 | for (int idx=0; idx. 18 | */ 19 | 20 | package org.jsl.tests.echo_latency; 21 | 22 | public class Main 23 | { 24 | public static void main( String args[] ) 25 | { 26 | int sessions = 1; 27 | int messages = 1000; 28 | int messageLength = 500; 29 | 30 | if (args.length > 0) 31 | sessions = Integer.parseInt( args[0] ); 32 | 33 | if (args.length > 1) 34 | messages = Integer.parseInt( args[1] ); 35 | 36 | if (args.length > 2) 37 | messageLength = Integer.parseInt( args[2] ); 38 | 39 | System.out.println( 40 | "Echo latency test: " + 41 | sessions + " sessions, " + 42 | messages + " messages, " + 43 | messageLength + " bytes/message." ); 44 | 45 | Client client = new Client( sessions, messages, messageLength ); 46 | new Server(client).run(); 47 | } 48 | } 49 | -------------------------------------------------------------------------------- /tests/src/org/jsl/tests/session_latency/Main.java: -------------------------------------------------------------------------------- 1 | /* 2 | * JS-Collider framework tests. 3 | * Copyright (C) 2013 Sergey Zubarev 4 | * info@js-labs.org 5 | * 6 | * This program is free software: you can redistribute it and/or modify 7 | * it under the terms of the GNU Affero General Public License as 8 | * published by the Free Software Foundation, either version 3 of the 9 | * License, or (at your option) any later version. 10 | * 11 | * This program is distributed in the hope that it will be useful, 12 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 13 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 | * GNU Affero General Public License for more details. 15 | * 16 | * You should have received a copy of the GNU Affero General Public License 17 | * along with this program. If not, see . 18 | */ 19 | 20 | package org.jsl.tests.session_latency; 21 | 22 | public class Main 23 | { 24 | public static void main( String [] args ) 25 | { 26 | int sessions = 2; 27 | int messages = 1000; 28 | int messageLength = 500; 29 | 30 | if (args.length > 0) 31 | sessions = Integer.parseInt( args[0] ); 32 | 33 | if (args.length > 1) 34 | messages = Integer.parseInt( args[1] ); 35 | 36 | if (args.length > 2) 37 | messageLength = Integer.parseInt( args[2] ); 38 | 39 | System.out.println( 40 | "Latency test: " + 41 | sessions + " sessions, " + 42 | messages + " messages, " + 43 | messageLength + " bytes/message." ); 44 | 45 | final Client client = new Client( sessions ); 46 | new Server(sessions, messages, messageLength, client).run(); 47 | } 48 | } 49 | -------------------------------------------------------------------------------- /src/main/java/org/jsl/collider/DatagramListener.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2013 Sergey Zubarev, info@js-labs.org 3 | * 4 | * This file is a part of JS-Collider framework. 5 | * 6 | * This program is free software: you can redistribute it and/or modify 7 | * it under the terms of the GNU Affero General Public License as 8 | * published by the Free Software Foundation, either version 3 of the 9 | * License, or (at your option) any later version. 10 | * 11 | * This program is distributed in the hope that it will be useful, 12 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 13 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 | * GNU Affero General Public License for more details. 15 | * 16 | * You should have received a copy of the GNU Affero General Public License 17 | * along with this program. If not, see . 18 | */ 19 | 20 | package org.jsl.collider; 21 | 22 | import java.net.InetSocketAddress; 23 | import java.net.SocketAddress; 24 | import java.nio.ByteBuffer; 25 | 26 | public abstract class DatagramListener 27 | { 28 | private final InetSocketAddress m_addr; 29 | 30 | public int socketRecvBufSize; 31 | public int inputQueueBlockSize; 32 | public int forwardReadMaxSize; 33 | public int readMinSize; 34 | 35 | public DatagramListener(InetSocketAddress addr) 36 | { 37 | m_addr = addr; 38 | 39 | /* use collider global default values */ 40 | socketRecvBufSize = 0; 41 | inputQueueBlockSize = 0; 42 | forwardReadMaxSize = 0; 43 | readMinSize = 0; 44 | } 45 | 46 | public InetSocketAddress getAddr() 47 | { 48 | return m_addr; 49 | } 50 | 51 | public abstract void onDataReceived(RetainableByteBuffer data, SocketAddress sourceAddr); 52 | } 53 | -------------------------------------------------------------------------------- /tests/src/org/jsl/tests/echo_throughput/Main.java: -------------------------------------------------------------------------------- 1 | /* 2 | * JS-Collider framework tests. 3 | * Copyright (C) 2013 Sergey Zubarev 4 | * info@js-labs.org 5 | * 6 | * This program is free software: you can redistribute it and/or modify 7 | * it under the terms of the GNU Affero General Public License as 8 | * published by the Free Software Foundation, either version 3 of the 9 | * License, or (at your option) any later version. 10 | * 11 | * This program is distributed in the hope that it will be useful, 12 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 13 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 | * GNU Affero General Public License for more details. 15 | * 16 | * You should have received a copy of the GNU Affero General Public License 17 | * along with this program. If not, see . 18 | */ 19 | 20 | package org.jsl.tests.echo_throughput; 21 | 22 | public class Main 23 | { 24 | public static void main( String [] args ) 25 | { 26 | int sessions = 1; 27 | int messages = 100000; 28 | int messageLength = 500; 29 | int socketBufferSize = 64*1024; 30 | 31 | if (args.length > 0) 32 | sessions = Integer.parseInt( args[0] ); 33 | 34 | if (args.length > 1) 35 | messages = Integer.parseInt( args[1] ); 36 | 37 | if (args.length > 2) 38 | messageLength = Integer.parseInt( args[2] ); 39 | 40 | System.out.println( 41 | "Echo throughput test: " + 42 | sessions + " sessions, " + 43 | messages + " messages, " + 44 | messageLength + " bytes/message." ); 45 | 46 | Client client = new Client( sessions, messages, messageLength, socketBufferSize ); 47 | new Server(client).run( socketBufferSize ); 48 | } 49 | } 50 | -------------------------------------------------------------------------------- /src/main/java/org/jsl/collider/Connector.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2013 Sergey Zubarev, info@js-labs.org 3 | * 4 | * This file is a part of JS-Collider framework. 5 | * 6 | * This program is free software: you can redistribute it and/or modify 7 | * it under the terms of the GNU Affero General Public License as 8 | * published by the Free Software Foundation, either version 3 of the 9 | * License, or (at your option) any later version. 10 | * 11 | * This program is distributed in the hope that it will be useful, 12 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 13 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 | * GNU Affero General Public License for more details. 15 | * 16 | * You should have received a copy of the GNU Affero General Public License 17 | * along with this program. If not, see . 18 | */ 19 | 20 | package org.jsl.collider; 21 | 22 | import java.io.IOException; 23 | import java.net.InetSocketAddress; 24 | 25 | public abstract class Connector extends SessionEmitter 26 | { 27 | public Connector(InetSocketAddress addr) 28 | { 29 | super(addr); 30 | } 31 | 32 | /** 33 | * Creates Session.Listener instance to be linked with the session. 34 | * Called by framework, derived class supposed to override the method. 35 | * Connection will be closed if returns null, but any data 36 | * scheduled with sendData call before return will be sent. 37 | */ 38 | public abstract Session.Listener createSessionListener(Session session); 39 | 40 | /** 41 | * Called by framework in a case if asynchronous operation 42 | * with underlying socket channel throws some exception. 43 | * @param ex An exception thrown by asynchronous I/O operation 44 | */ 45 | public abstract void onException(IOException ex); 46 | } 47 | -------------------------------------------------------------------------------- /tests/src/org/jsl/tests/send_throughput/Main.java: -------------------------------------------------------------------------------- 1 | /* 2 | * JS-Collider framework. 3 | * Copyright (C) 2013 Sergey Zubarev 4 | * info@js-labs.org 5 | * 6 | * This program is free software: you can redistribute it and/or modify 7 | * it under the terms of the GNU Affero General Public License as 8 | * published by the Free Software Foundation, either version 3 of the 9 | * License, or (at your option) any later version. 10 | * 11 | * This program is distributed in the hope that it will be useful, 12 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 13 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 | * GNU Affero General Public License for more details. 15 | * 16 | * You should have received a copy of the GNU Affero General Public License 17 | * along with this program. If not, see . 18 | */ 19 | 20 | package org.jsl.tests.send_throughput; 21 | 22 | public class Main 23 | { 24 | public static void main(String [] args) 25 | { 26 | final int port = 0; // system assigned free port will be used 27 | int sessions = 1; 28 | int messages = 1000000; 29 | int messageLength = 500; 30 | int socketBufferSize = (64 * 1024); 31 | 32 | if (args.length > 0) 33 | { 34 | sessions = Integer.parseInt(args[0]); 35 | if (args.length > 1) 36 | { 37 | messages = Integer.parseInt(args[1]); 38 | if (args.length > 2) 39 | { 40 | messageLength = Integer.parseInt(args[2]); 41 | if (args.length > 3) 42 | socketBufferSize = Integer.parseInt(args[3]); 43 | } 44 | } 45 | } 46 | 47 | final Client client = new Client(sessions, messages, messageLength, socketBufferSize); 48 | final Server server = new Server(); 49 | server.run(port, socketBufferSize, client); 50 | client.stopAndWait(); 51 | } 52 | } 53 | -------------------------------------------------------------------------------- /tests/src/org/jsl/tests/thread_pool_throughput/Main.java: -------------------------------------------------------------------------------- 1 | /* 2 | * JS-Collider framework. 3 | * Copyright (C) 2013 Sergey Zubarev 4 | * info@js-labs.org 5 | * 6 | * This program is free software: you can redistribute it and/or modify 7 | * it under the terms of the GNU Affero General Public License as 8 | * published by the Free Software Foundation, either version 3 of the 9 | * License, or (at your option) any later version. 10 | * 11 | * This program is distributed in the hope that it will be useful, 12 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 13 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 | * GNU Affero General Public License for more details. 15 | * 16 | * You should have received a copy of the GNU Affero General Public License 17 | * along with this program. If not, see . 18 | */ 19 | 20 | package org.jsl.tests.thread_pool_throughput; 21 | 22 | public class Main 23 | { 24 | private Main() 25 | { 26 | } 27 | 28 | public void run() 29 | { 30 | int TOTAL_EVENTS = 1000000; 31 | 32 | Test [] tests = 33 | { 34 | new ThreadPoolTest( TOTAL_EVENTS, 1, 1 ), 35 | new ThreadPoolTest( TOTAL_EVENTS, 4, 4 ), 36 | new ExecutorTest(TOTAL_EVENTS, 1, 1), 37 | new ExecutorTest( TOTAL_EVENTS, 4, 4 ) 38 | }; 39 | 40 | for (Test test : tests) 41 | { 42 | System.out.println( test.getName() + ":" ); 43 | long tm = test.runTest(); 44 | tm /= 1000; 45 | System.out.println( TOTAL_EVENTS + " events processed at " + 46 | String.format( "%d.%06d", tm/1000000, tm%1000000 ) + " sec: " + 47 | test.getProducers() + " -> " + 48 | test.getWorkers() + " workers." ); 49 | } 50 | } 51 | 52 | public static void main( String [] args ) 53 | { 54 | new Main().run(); 55 | } 56 | } 57 | -------------------------------------------------------------------------------- /tests/src/org/jsl/tests/session_throughput/Main.java: -------------------------------------------------------------------------------- 1 | /* 2 | * JS-Collider framework tests. 3 | * Copyright (C) 2013 Sergey Zubarev 4 | * info@js-labs.org 5 | * 6 | * This program is free software: you can redistribute it and/or modify 7 | * it under the terms of the GNU Affero General Public License as 8 | * published by the Free Software Foundation, either version 3 of the 9 | * License, or (at your option) any later version. 10 | * 11 | * This program is distributed in the hope that it will be useful, 12 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 13 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 | * GNU Affero General Public License for more details. 15 | * 16 | * You should have received a copy of the GNU Affero General Public License 17 | * along with this program. If not, see . 18 | */ 19 | 20 | package org.jsl.tests.session_throughput; 21 | 22 | /* Client connect to the server and send some messages, 23 | * server transmit each message received from client to all connected clients. 24 | */ 25 | public class Main 26 | { 27 | public static void main( String args[] ) 28 | { 29 | int sessions = 2; 30 | int messages = 100000; 31 | int messageLength = 500; 32 | int socketBufferSize = (64 * 1024); 33 | 34 | if (args.length > 0) 35 | sessions = Integer.parseInt( args[0] ); 36 | 37 | if (args.length > 1) 38 | messages = Integer.parseInt( args[1] ); 39 | 40 | if (args.length > 2) 41 | messageLength = Integer.parseInt( args[2] ); 42 | 43 | System.out.println( 44 | "Session throughput test: " + 45 | sessions + " sessions, " + 46 | messages + " messages, " + 47 | messageLength + " bytes/message." ); 48 | 49 | Client client = new Client( sessions, messages, messageLength, socketBufferSize ); 50 | new Server(client, socketBufferSize).run(); 51 | } 52 | } 53 | -------------------------------------------------------------------------------- /tests/src/org/jsl/tests/sched_latency/Main.java: -------------------------------------------------------------------------------- 1 | /* 2 | * JS-Collider framework. 3 | * Copyright (C) 2013 Sergey Zubarev 4 | * info@js-labs.org 5 | * 6 | * This program is free software: you can redistribute it and/or modify 7 | * it under the terms of the GNU Affero General Public License as 8 | * published by the Free Software Foundation, either version 3 of the 9 | * License, or (at your option) any later version. 10 | * 11 | * This program is distributed in the hope that it will be useful, 12 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 13 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 | * GNU Affero General Public License for more details. 15 | * 16 | * You should have received a copy of the GNU Affero General Public License 17 | * along with this program. If not, see . 18 | */ 19 | 20 | package org.jsl.tests.sched_latency; 21 | 22 | import java.util.Arrays; 23 | 24 | public class Main 25 | { 26 | public static void printResult(String str, long [] res) 27 | { 28 | final int c = Math.min(20, res.length); 29 | for (int idx=0; idx. 18 | */ 19 | 20 | package org.jsl.tests.queue_socket_send; 21 | 22 | public class Main 23 | { 24 | public static void main( String [] args ) 25 | { 26 | int sessions = 1; 27 | int messages = 100000; 28 | int messageLength = 200; 29 | int socketBufferSize = 64*1024; 30 | 31 | if (args.length > 0) 32 | { 33 | sessions = Integer.parseInt( args[0] ); 34 | if (args.length > 1) 35 | { 36 | messages = Integer.parseInt( args[1] ); 37 | if (args.length > 2) 38 | { 39 | messageLength = Integer.parseInt( args[2] ); 40 | if (args.length > 3) 41 | socketBufferSize = Integer.parseInt( args[3] ); 42 | } 43 | } 44 | } 45 | 46 | System.out.println( "Sessions=" + sessions ); 47 | System.out.println( "MessageLength=" + messageLength ); 48 | 49 | BufferCopySender sender2 = new BufferCopySender( sessions, messages, messageLength, socketBufferSize ); 50 | sender2.run(); 51 | 52 | try { Thread.sleep(1000); } 53 | catch (InterruptedException ex) { ex.printStackTrace(); } 54 | System.out.println("****"); 55 | 56 | Sender sender1 = new BufferDuplicateSender( sessions, messages, messageLength, socketBufferSize ); 57 | sender1.run(); 58 | } 59 | } 60 | -------------------------------------------------------------------------------- /tests/src/org/jsl/tests/unit/Main.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2013 Sergey Zubarev, info@js-labs.org 3 | * 4 | * This file is a part of JS-Collider test. 5 | * 6 | * This program is free software: you can redistribute it and/or modify 7 | * it under the terms of the GNU Affero General Public License as 8 | * published by the Free Software Foundation, either version 3 of the 9 | * License, or (at your option) any later version. 10 | * 11 | * This program is distributed in the hope that it will be useful, 12 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 13 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 | * GNU Affero General Public License for more details. 15 | * 16 | * You should have received a copy of the GNU Affero General Public License 17 | * along with this program. If not, see . 18 | */ 19 | package org.jsl.tests.unit; 20 | 21 | import java.nio.ByteBuffer; 22 | import java.nio.ByteOrder; 23 | import org.jsl.collider.DataBlock; 24 | 25 | interface TestFunc { 26 | void run() throws Exception; 27 | } 28 | 29 | public class Main { 30 | private static void dataBlockInheritsByteOrder() throws Exception { 31 | final ByteOrder [] orders = {ByteOrder.LITTLE_ENDIAN, ByteOrder.BIG_ENDIAN}; 32 | for (ByteOrder byteOrder: orders) { 33 | final ByteBuffer byteBuffer = ByteBuffer.allocateDirect(32); 34 | byteBuffer.order(byteOrder); 35 | final DataBlock dataBlock = new DataBlock(byteBuffer); 36 | if (dataBlock.rd.order() != byteOrder) { 37 | throw new Exception("wrong dataBlock.rd.order()"); 38 | } 39 | if (dataBlock.wr.order() != byteOrder) { 40 | throw new Exception("wrong dataBlock.wr.order()"); 41 | } 42 | } 43 | } 44 | 45 | private static int runTest(TestFunc testFunc) { 46 | try { 47 | testFunc.run(); 48 | return 0; 49 | } catch (final Exception ex) { 50 | System.out.println(ex.getMessage()); 51 | return 1; 52 | } 53 | } 54 | 55 | public static void main(String [] args) { 56 | int failedTests = 0; 57 | failedTests += runTest(Main::dataBlockInheritsByteOrder); 58 | System.out.println(failedTests + " tests failed"); 59 | System.exit((failedTests == 0) ? 0 : -1); 60 | } 61 | } 62 | -------------------------------------------------------------------------------- /src/main/java/org/jsl/collider/ShMemServer.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2013 Sergey Zubarev, info@js-labs.org 3 | * 4 | * This file is a part of JS-Collider framework. 5 | * 6 | * This program is free software: you can redistribute it and/or modify 7 | * it under the terms of the GNU Affero General Public License as 8 | * published by the Free Software Foundation, either version 3 of the 9 | * License, or (at your option) any later version. 10 | * 11 | * This program is distributed in the hope that it will be useful, 12 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 13 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 | * GNU Affero General Public License for more details. 15 | * 16 | * You should have received a copy of the GNU Affero General Public License 17 | * along with this program. If not, see . 18 | */ 19 | 20 | package org.jsl.collider; 21 | 22 | import java.io.File; 23 | import java.nio.charset.Charset; 24 | import java.nio.charset.CharsetDecoder; 25 | 26 | public class ShMemServer extends ShMem 27 | { 28 | private final ChannelIn m_in; 29 | private final ChannelOut m_out; 30 | 31 | public ShMemServer( RetainableByteBuffer buf ) throws Exception 32 | { 33 | final short descriptorVersion = buf.getShort(); 34 | final int bufLimit = buf.limit(); 35 | if (descriptorVersion != 1) 36 | throw new Exception( "ShMem descriptor version " + descriptorVersion + " not supported." ); 37 | 38 | final CharsetDecoder decoder = Charset.defaultCharset().newDecoder(); 39 | final int blockSize = buf.getInt(); 40 | 41 | final int length = buf.getShort(); 42 | buf.limit( buf.position() + length ); 43 | final File fileC2S = new File( decoder.decode(buf.getNioByteBuffer()).toString() ); 44 | m_in = new ChannelIn( fileC2S, blockSize, false ); 45 | 46 | buf.limit( bufLimit ); 47 | buf.getShort(); 48 | final File fileS2C = new File( decoder.decode(buf.getNioByteBuffer()).toString() ); 49 | m_out = new ChannelOut( fileS2C, blockSize, false ); 50 | } 51 | 52 | public ChannelIn getIn() 53 | { 54 | return m_in; 55 | } 56 | 57 | public ChannelOut getOut() 58 | { 59 | return m_out; 60 | } 61 | 62 | public void close() 63 | { 64 | m_in.close(); 65 | m_out.close(); 66 | } 67 | } 68 | -------------------------------------------------------------------------------- /src/main/java/org/jsl/collider/RetainableDataBlock.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2013 Sergey Zubarev, info@js-labs.org 3 | * 4 | * This file is a part of JS-Collider framework. 5 | * 6 | * This program is free software: you can redistribute it and/or modify 7 | * it under the terms of the GNU Affero General Public License as 8 | * published by the Free Software Foundation, either version 3 of the 9 | * License, or (at your option) any later version. 10 | * 11 | * This program is distributed in the hope that it will be useful, 12 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 13 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 | * GNU Affero General Public License for more details. 15 | * 16 | * You should have received a copy of the GNU Affero General Public License 17 | * along with this program. If not, see . 18 | */ 19 | package org.jsl.collider; 20 | 21 | import java.nio.ByteBuffer; 22 | 23 | public abstract class RetainableDataBlock 24 | { 25 | /* RetainableDataBlock: and shares the same memory block, 26 | * used by socket reader thread, used by data processing thread. 27 | */ 28 | public RetainableDataBlock next; 29 | public final ByteBuffer wr; 30 | public final RetainableByteBuffer rd; 31 | 32 | private static class BufferImpl extends RetainableByteBufferImpl 33 | { 34 | private final RetainableDataBlock m_dataBlock; 35 | 36 | public BufferImpl( ByteBuffer byteBuffer, RetainableDataBlock dataBlock ) 37 | { 38 | super( byteBuffer ); 39 | m_dataBlock = dataBlock; 40 | } 41 | 42 | protected void finalRelease() 43 | { 44 | m_dataBlock.finalRelease(); 45 | } 46 | } 47 | 48 | protected final void reinit() 49 | { 50 | assert(next == null); 51 | wr.clear(); 52 | rd.reinit(); 53 | } 54 | 55 | protected abstract void finalRelease(); 56 | 57 | public RetainableDataBlock( ByteBuffer byteBuffer ) 58 | { 59 | wr = byteBuffer; 60 | rd = new BufferImpl(byteBuffer.duplicate(), this); 61 | } 62 | 63 | public final void release() 64 | { 65 | rd.release(); 66 | } 67 | 68 | public final boolean clearSafe() 69 | { 70 | final boolean ret = rd.clearSafe(); 71 | if (ret) 72 | wr.clear(); 73 | return ret; 74 | } 75 | } 76 | -------------------------------------------------------------------------------- /src/main/java/org/jsl/collider/RetainableByteBufferCache.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2013 Sergey Zubarev, info@js-labs.org 3 | * 4 | * This file is a part of JS-Collider framework. 5 | * 6 | * This program is free software: you can redistribute it and/or modify 7 | * it under the terms of the GNU Affero General Public License as 8 | * published by the Free Software Foundation, either version 3 of the 9 | * License, or (at your option) any later version. 10 | * 11 | * This program is distributed in the hope that it will be useful, 12 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 13 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 | * GNU Affero General Public License for more details. 15 | * 16 | * You should have received a copy of the GNU Affero General Public License 17 | * along with this program. If not, see . 18 | */ 19 | 20 | package org.jsl.collider; 21 | 22 | import java.nio.ByteBuffer; 23 | import java.nio.ByteOrder; 24 | 25 | public class RetainableByteBufferCache extends ObjectCache 26 | { 27 | private static class BufferImpl extends RetainableByteBufferImpl 28 | { 29 | private RetainableByteBufferCache m_cache; 30 | 31 | BufferImpl(ByteBuffer buf, RetainableByteBufferCache cache) 32 | { 33 | super(buf); 34 | m_cache = cache; 35 | } 36 | 37 | protected void finalRelease() 38 | { 39 | reinit(); 40 | m_cache.put(this); 41 | } 42 | } 43 | 44 | private final boolean m_useDirectBuffer; 45 | private final int m_bufferCapacity; 46 | private final ByteOrder m_byteOrder; 47 | 48 | protected BufferImpl allocateObject() 49 | { 50 | final ByteBuffer byteBuffer = 51 | m_useDirectBuffer 52 | ? ByteBuffer.allocateDirect(m_bufferCapacity) 53 | : ByteBuffer.allocate(m_bufferCapacity); 54 | byteBuffer.order(m_byteOrder); 55 | return new BufferImpl(byteBuffer, this); 56 | } 57 | 58 | public RetainableByteBufferCache(boolean useDirectBuffer, int bufferCapacity, ByteOrder byteOrder, int size) 59 | { 60 | super(RetainableByteBufferCache.class.getSimpleName() + "-" + bufferCapacity, new RetainableByteBuffer[size]); 61 | m_useDirectBuffer = useDirectBuffer; 62 | m_bufferCapacity = bufferCapacity; 63 | m_byteOrder = byteOrder; 64 | } 65 | } 66 | -------------------------------------------------------------------------------- /tests/src/org/jsl/tests/binary_queue/Generator.java: -------------------------------------------------------------------------------- 1 | /* 2 | * JS-Collider framework. 3 | * Copyright (C) 2013 Sergey Zubarev 4 | * info@js-labs.org 5 | * 6 | * This program is free software: you can redistribute it and/or modify 7 | * it under the terms of the GNU General Public License as published by 8 | * the Free Software Foundation, either version 3 of the License, or 9 | * (at your option) any later version. 10 | * This program is distributed in the hope that it will be useful, 11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. 13 | * See the GNU General Public License for more details. 14 | * You should have received a copy of the GNU General Public License 15 | * along with this program. If not, see . 16 | */ 17 | 18 | package org.jsl.tests.binary_queue; 19 | 20 | import org.jsl.tests.Util; 21 | import java.nio.ByteBuffer; 22 | 23 | public class Generator extends Thread 24 | { 25 | private Main m_main; 26 | private int m_msgs; 27 | private ByteBuffer m_msg; 28 | 29 | public Generator( Main main, int messages, int messageSize, int magic ) 30 | { 31 | m_main = main; 32 | m_msgs = messages; 33 | if (messageSize > 4) 34 | { 35 | if (messageSize < 8) 36 | messageSize = 8; 37 | m_msg = ByteBuffer.allocate( messageSize ); 38 | m_msg.putInt( messageSize ); 39 | m_msg.putInt( magic ); 40 | for (int idx=0; idx. 18 | */ 19 | 20 | package org.jsl.tests.sched_latency; 21 | 22 | import org.jsl.collider.ThreadPool; 23 | import java.util.concurrent.Semaphore; 24 | 25 | public class SL_ThreadPool 26 | { 27 | private final long [] m_res; 28 | private final Semaphore m_semDone; 29 | private ThreadPool [] m_threadPool; 30 | private long m_time; 31 | private int m_idx; 32 | 33 | private class TestRunnable extends ThreadPool.Runnable 34 | { 35 | public void runInThreadPool() 36 | { 37 | if (m_idx < m_res.length) 38 | { 39 | long time = System.nanoTime(); 40 | m_res[m_idx] = (time - m_time); 41 | m_idx++; 42 | m_time = time; 43 | m_threadPool[m_idx%2].execute( this ); 44 | } 45 | else 46 | m_semDone.release(); 47 | } 48 | } 49 | 50 | public SL_ThreadPool( long [] res ) 51 | { 52 | m_res = res; 53 | m_semDone = new Semaphore(0); 54 | } 55 | 56 | public void start() 57 | { 58 | TestRunnable runnable = new TestRunnable(); 59 | m_threadPool = new ThreadPool[2]; 60 | m_threadPool[0] = new ThreadPool( "TP1", 1 ); 61 | m_threadPool[1] = new ThreadPool( "TP2", 1 ); 62 | m_threadPool[0].start(); 63 | m_threadPool[1].start(); 64 | 65 | m_time = System.nanoTime(); 66 | m_threadPool[0].execute( runnable ); 67 | 68 | try 69 | { 70 | m_semDone.acquire(1); 71 | m_threadPool[0].stopAndWait(); 72 | m_threadPool[1].stopAndWait(); 73 | } 74 | catch (InterruptedException ex) 75 | { 76 | System.out.println(ex); 77 | } 78 | } 79 | } 80 | -------------------------------------------------------------------------------- /src/main/java/org/jsl/collider/LogFormatter.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2022 Sergey Zubarev, info@js-labs.org 3 | * 4 | * This file is a part of JS-Collider framework. 5 | * 6 | * This program is free software: you can redistribute it and/or modify 7 | * it under the terms of the GNU Affero General Public License as 8 | * published by the Free Software Foundation, either version 3 of the 9 | * License, or (at your option) any later version. 10 | * 11 | * This program is distributed in the hope that it will be useful, 12 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 13 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 | * GNU Affero General Public License for more details. 15 | * 16 | * You should have received a copy of the GNU Affero General Public License 17 | * along with this program. If not, see . 18 | */ 19 | 20 | package org.jsl.collider; 21 | 22 | import java.text.SimpleDateFormat; 23 | import java.util.Calendar; 24 | import java.util.Date; 25 | import java.util.logging.Formatter; 26 | import java.util.logging.LogRecord; 27 | 28 | public class LogFormatter extends Formatter 29 | { 30 | private static String getPackageName() 31 | { 32 | Class cls = LogFormatter.class; 33 | String canonicalName = cls.getCanonicalName(); 34 | String simpleName = cls.getSimpleName(); 35 | int length = (canonicalName.length() - simpleName.length()); 36 | return canonicalName.substring( 0, length ); 37 | } 38 | 39 | public String format(LogRecord logRecord) 40 | { 41 | final StringBuilder sb = new StringBuilder(); 42 | final SimpleDateFormat simpleDateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss.SSS"); 43 | final Date date = new Date(logRecord.getMillis()); 44 | sb.append(simpleDateFormat.format(date)); 45 | sb.append("t@"); 46 | sb.append(logRecord.getThreadID()); 47 | sb.append(' '); 48 | sb.append(logRecord.getLevel().getName()); 49 | sb.append(' '); 50 | 51 | final String className = logRecord.getSourceClassName(); 52 | final int idx = className.lastIndexOf('.'); 53 | if (idx < 0) 54 | sb.append(className); 55 | else 56 | sb.append(className.substring(idx+1)); 57 | 58 | sb.append('.'); 59 | sb.append(logRecord.getSourceMethodName()); 60 | 61 | final String msg = logRecord.getMessage(); 62 | if (msg.length() > 0) 63 | { 64 | sb.append(": "); 65 | sb.append(logRecord.getMessage()); 66 | } 67 | 68 | sb.append('\n'); 69 | return sb.toString(); 70 | } 71 | } 72 | -------------------------------------------------------------------------------- /src/main/java/org/jsl/collider/Acceptor.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2013 Sergey Zubarev, info@js-labs.org 3 | * 4 | * This file is a part of JS-Collider framework. 5 | * 6 | * This program is free software: you can redistribute it and/or modify 7 | * it under the terms of the GNU Affero General Public License as 8 | * published by the Free Software Foundation, either version 3 of the 9 | * License, or (at your option) any later version. 10 | * 11 | * This program is distributed in the hope that it will be useful, 12 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 13 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 | * GNU Affero General Public License for more details. 15 | * 16 | * You should have received a copy of the GNU Affero General Public License 17 | * along with this program. If not, see . 18 | */ 19 | 20 | package org.jsl.collider; 21 | 22 | import java.net.InetSocketAddress; 23 | 24 | /** 25 | * {@code Acceptor} is used to accept inbound socket connections. 26 | * See {@link Collider} for details. 27 | */ 28 | 29 | public abstract class Acceptor extends SessionEmitter 30 | { 31 | /** 32 | * Initialize acceptor to listen on any available port. 33 | * The port number can be obtained later in the onAcceptorStarted() 34 | */ 35 | public Acceptor() 36 | { 37 | super(new InetSocketAddress(0)); 38 | } 39 | 40 | /** 41 | * Initialize acceptor to listen on the particular port. 42 | * @param listenPort TCP port number to accept connections on. 43 | */ 44 | public Acceptor(int listenPort) 45 | { 46 | super(new InetSocketAddress(listenPort)); 47 | } 48 | 49 | /** 50 | * Called by the Collider runtime to create Session.Listener instance 51 | * to be linked with the session and underlying socket. METHOD IS NOT [MT] SAFE, 52 | * can be executed concurrently in a number of threads. 53 | * Connection and underlying socket will be closed if returns null, 54 | * but any data scheduled with sendData still will be sent. 55 | */ 56 | public abstract Session.Listener createSessionListener(Session session); 57 | 58 | /** 59 | * Called by the Collider runtime right before the acceptor is ready to accept connections. 60 | * It is still safe to remove the Acceptor instance from the collider 61 | * within this method, no one connection will be accepted then. 62 | * @param collider the collider instance to work in 63 | * @param localPort the local port to accept connections on 64 | */ 65 | public void onAcceptorStarted(Collider collider, int localPort) 66 | { 67 | } 68 | } 69 | -------------------------------------------------------------------------------- /src/main/java/org/jsl/collider/SessionEmitter.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2013 Sergey Zubarev, info@js-labs.org 3 | * 4 | * This file is a part of JS-Collider framework. 5 | * 6 | * This program is free software: you can redistribute it and/or modify 7 | * it under the terms of the GNU Affero General Public License as 8 | * published by the Free Software Foundation, either version 3 of the 9 | * License, or (at your option) any later version. 10 | * 11 | * This program is distributed in the hope that it will be useful, 12 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 13 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 | * GNU Affero General Public License for more details. 15 | * 16 | * You should have received a copy of the GNU Affero General Public License 17 | * along with this program. If not, see . 18 | */ 19 | 20 | package org.jsl.collider; 21 | 22 | import java.net.InetSocketAddress; 23 | import java.nio.ByteOrder; 24 | 25 | public abstract class SessionEmitter 26 | { 27 | private final InetSocketAddress m_addr; 28 | 29 | /* > 0 : use direct buffers 30 | * == 0 : use heap buffers 31 | * < 0 : use collider default buffer type 32 | */ 33 | public int useDirectBuffers; 34 | public ByteOrder byteOrder; 35 | 36 | public boolean reuseAddr; 37 | public boolean tcpNoDelay; 38 | 39 | public int socketRecvBufSize; 40 | public int socketSendBufSize; 41 | public int forwardReadMaxSize; 42 | public int inputQueueBlockSize; 43 | 44 | public int joinMessageMaxSize; 45 | 46 | public SessionEmitter(InetSocketAddress addr) 47 | { 48 | m_addr = addr; 49 | 50 | useDirectBuffers = -1; 51 | byteOrder = null; 52 | 53 | reuseAddr = false; 54 | tcpNoDelay = true; 55 | 56 | /* Use collider global settings by default */ 57 | socketRecvBufSize = 0; 58 | socketSendBufSize = 0; 59 | forwardReadMaxSize = 0; 60 | inputQueueBlockSize = 0; 61 | 62 | /* -1 - use collider global value, 63 | * 0 - disable message join, 64 | */ 65 | joinMessageMaxSize = -1; 66 | } 67 | 68 | public InetSocketAddress getAddr() 69 | { 70 | return m_addr; 71 | } 72 | 73 | /** 74 | * Called by the Collider runtime to create session listener instance. 75 | * See Acceptor.createSessionListener and 76 | * Connector.createSessionListener for detailed description. 77 | * @param session session the listener will be used for 78 | * @return A listener object for the given session 79 | */ 80 | public abstract Session.Listener createSessionListener(Session session); 81 | } 82 | -------------------------------------------------------------------------------- /tests/src/org/jsl/tests/sched_latency/SL_Semaphore.java: -------------------------------------------------------------------------------- 1 | /* 2 | * JS-Collider framework. 3 | * Copyright (C) 2013 Sergey Zubarev 4 | * info@js-labs.org 5 | * 6 | * This program is free software: you can redistribute it and/or modify 7 | * it under the terms of the GNU Affero General Public License as 8 | * published by the Free Software Foundation, either version 3 of the 9 | * License, or (at your option) any later version. 10 | * 11 | * This program is distributed in the hope that it will be useful, 12 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 13 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 | * GNU Affero General Public License for more details. 15 | * 16 | * You should have received a copy of the GNU Affero General Public License 17 | * along with this program. If not, see . 18 | */ 19 | 20 | package org.jsl.tests.sched_latency; 21 | 22 | import java.util.concurrent.Semaphore; 23 | 24 | public class SL_Semaphore 25 | { 26 | private final long [] m_res; 27 | private final Semaphore m_semDone; 28 | private long m_time; 29 | private int m_idx; 30 | 31 | private class TestThread implements Runnable 32 | { 33 | private final Semaphore m_sem1; 34 | private final Semaphore m_sem2; 35 | 36 | public TestThread( Semaphore sem1, Semaphore sem2 ) 37 | { 38 | m_sem1 = sem1; 39 | m_sem2 = sem2; 40 | } 41 | 42 | public void run() 43 | { 44 | for (;;) 45 | { 46 | try { m_sem1.acquire(); } 47 | catch (InterruptedException ex) { System.out.println(ex); } 48 | 49 | if (m_idx == m_res.length) 50 | break; 51 | 52 | long time = System.nanoTime(); 53 | m_res[m_idx] = (time - m_time); 54 | m_idx++; 55 | m_time = time; 56 | 57 | m_sem2.release(); 58 | } 59 | 60 | m_sem2.release(); 61 | m_semDone.release(); 62 | } 63 | } 64 | 65 | public SL_Semaphore( long [] res ) 66 | { 67 | m_res = res; 68 | m_semDone = new Semaphore(0); 69 | } 70 | 71 | public void start() 72 | { 73 | Semaphore sem1 = new Semaphore(0); 74 | Semaphore sem2 = new Semaphore(0); 75 | 76 | m_time = System.nanoTime(); 77 | new Thread(new TestThread(sem1, sem2)).start(); 78 | new Thread(new TestThread(sem2, sem1)).start(); 79 | 80 | m_time = System.nanoTime(); 81 | sem1.release(); 82 | 83 | try { m_semDone.acquire(2); } 84 | catch (InterruptedException ex) { System.out.println(ex); } 85 | } 86 | } 87 | 88 | -------------------------------------------------------------------------------- /pom.xml: -------------------------------------------------------------------------------- 1 | 3 | 4.0.0 4 | 5 | org.js-labs 6 | js-collider 7 | jar 8 | 9 | js-collider 10 | 0.2.6 11 | Java NIO framework 12 | https://github.com/js-labs/js-collider 13 | 14 | 15 | 16 | GNU Affero General Public License 17 | http://www.gnu.org/licenses/agpl-3.0.txt 18 | repo 19 | GNU Affero General Public License 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | scm:git:https://github.com/js-labs/js-collider.git 30 | v${project.version} 31 | https://github.com/js-labs/js-collider 32 | 33 | 34 | 35 | UTF-8 36 | 37 | 38 | 39 | 40 | junit 41 | junit 42 | 3.8.1 43 | test 44 | 45 | 46 | 47 | 48 | 49 | 50 | org.apache.maven.plugins 51 | maven-source-plugin 52 | 2.4 53 | 54 | 55 | attach-sources 56 | 57 | jar 58 | 59 | 60 | 61 | 62 | 63 | org.apache.maven.plugins 64 | maven-javadoc-plugin 65 | 2.10.3 66 | 67 | 68 | attach-docs 69 | 70 | jar 71 | 72 | 73 | 74 | 75 | 76 | org.apache.maven.plugins 77 | maven-deploy-plugin 78 | 2.8.2 79 | 80 | local::default::file://${basedir}/target/repo 81 | 82 | 83 | 84 | 85 | 86 | 87 | -------------------------------------------------------------------------------- /tests/src/org/jsl/tests/thread_pool/Main.java: -------------------------------------------------------------------------------- 1 | /* 2 | * JS-Collider framework. 3 | * Copyright (C) 2013 Sergey Zubarev 4 | * info@js-labs.org 5 | * 6 | * This program is free software: you can redistribute it and/or modify 7 | * it under the terms of the GNU Affero General Public License as 8 | * published by the Free Software Foundation, either version 3 of the 9 | * License, or (at your option) any later version. 10 | * 11 | * This program is distributed in the hope that it will be useful, 12 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 13 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 | * GNU Affero General Public License for more details. 15 | * 16 | * You should have received a copy of the GNU Affero General Public License 17 | * along with this program. If not, see . 18 | */ 19 | 20 | package org.jsl.tests.thread_pool; 21 | 22 | import org.jsl.collider.ThreadPool; 23 | import java.util.concurrent.CountDownLatch; 24 | 25 | public class Main 26 | { 27 | private final Runnable [] m_task; 28 | private final CountDownLatch m_done; 29 | private final ThreadPool m_threadPool; 30 | 31 | private class Runnable extends ThreadPool.Runnable 32 | { 33 | private Thread m_worker; 34 | private int m_events; 35 | 36 | public Runnable( int events ) 37 | { 38 | m_events = events; 39 | } 40 | 41 | public void runInThreadPool() 42 | { 43 | assert( m_worker == null ); 44 | m_worker = Thread.currentThread(); 45 | if (--m_events == 0) 46 | { 47 | m_worker = null; 48 | m_done.countDown(); 49 | } 50 | else 51 | { 52 | m_worker = null; 53 | m_threadPool.execute( this ); 54 | } 55 | } 56 | } 57 | 58 | private Main( int tasks, int events ) 59 | { 60 | m_task = new Runnable[tasks]; 61 | for (int idx=0; idx. 18 | */ 19 | 20 | package org.jsl.collider; 21 | 22 | import java.util.concurrent.atomic.AtomicLongArray; 23 | 24 | public class StatCounter 25 | { 26 | /* 0 - min 27 | * 1 - avg 28 | * 2 - max 29 | * 3 - sum 30 | */ 31 | private final String m_name; 32 | private final AtomicLongArray m_data; 33 | 34 | public StatCounter( String name ) 35 | { 36 | m_name = name; 37 | m_data = new AtomicLongArray(4); 38 | m_data.set( 0, Long.MAX_VALUE ); 39 | } 40 | 41 | public final void trace( final long value ) 42 | { 43 | for (;;) 44 | { 45 | long min = m_data.get(0); 46 | if (value >= min) 47 | break; 48 | if (m_data.compareAndSet(0, min, value)) 49 | break; 50 | } 51 | 52 | for (;;) 53 | { 54 | final long avg = m_data.get(1); 55 | long cnt = (avg >> 32); 56 | long newAvg = cnt * (avg & 0xFFFFFFFFL) + value; 57 | cnt++; 58 | newAvg /= cnt; 59 | newAvg &= 0xFFFFFFFFL; 60 | newAvg |= (cnt << 32); 61 | if (m_data.compareAndSet(1, avg, newAvg)) 62 | break; 63 | } 64 | 65 | for (;;) 66 | { 67 | long max = m_data.get(2); 68 | if (value <= max) 69 | break; 70 | if (m_data.compareAndSet(2, max, value)) 71 | break; 72 | } 73 | 74 | for (;;) 75 | { 76 | long total = m_data.get(3); 77 | if (m_data.compareAndSet(3, total, total+value)) 78 | break; 79 | } 80 | } 81 | 82 | public final String getStats() 83 | { 84 | String str = m_name; 85 | str += ": min=" + m_data.get(0); 86 | str += " avg=" + (m_data.get(1) & 0xFFFFFFFFL); 87 | str += " max=" + m_data.get(2); 88 | str += " sum=" + m_data.get(3); 89 | str += " cnt=" + (m_data.get(1) >> 32); 90 | return str; 91 | } 92 | } 93 | -------------------------------------------------------------------------------- /tests/src/org/jsl/tests/sched_latency/SL_Executor.java: -------------------------------------------------------------------------------- 1 | /* 2 | * JS-Collider framework. 3 | * Copyright (C) 2013 Sergey Zubarev 4 | * info@js-labs.org 5 | * 6 | * This program is free software: you can redistribute it and/or modify 7 | * it under the terms of the GNU Affero General Public License as 8 | * published by the Free Software Foundation, either version 3 of the 9 | * License, or (at your option) any later version. 10 | * 11 | * This program is distributed in the hope that it will be useful, 12 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 13 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 | * GNU Affero General Public License for more details. 15 | * 16 | * You should have received a copy of the GNU Affero General Public License 17 | * along with this program. If not, see . 18 | */ 19 | 20 | package org.jsl.tests.sched_latency; 21 | 22 | import java.util.concurrent.ExecutorService; 23 | import java.util.concurrent.Executors; 24 | import java.util.concurrent.Semaphore; 25 | import java.util.concurrent.TimeUnit; 26 | 27 | 28 | public class SL_Executor 29 | { 30 | private final long [] m_res; 31 | private final Semaphore m_semDone; 32 | private final ExecutorService [] m_executor; 33 | private long m_time; 34 | private int m_idx; 35 | 36 | private class TestRunnable implements Runnable 37 | { 38 | public void run() 39 | { 40 | if (m_idx < m_res.length) 41 | { 42 | long time = System.nanoTime(); 43 | m_res[m_idx] = (time - m_time); 44 | m_idx++; 45 | m_time = time; 46 | m_executor[m_idx%2].execute( this ); 47 | } 48 | else 49 | m_semDone.release(); 50 | } 51 | } 52 | 53 | public SL_Executor( long [] res ) 54 | { 55 | m_res = res; 56 | m_semDone = new Semaphore(0); 57 | m_executor = new ExecutorService[2]; 58 | m_executor[0] = Executors.newFixedThreadPool(1); 59 | m_executor[1] = Executors.newFixedThreadPool(1); 60 | } 61 | 62 | public void start() 63 | { 64 | TestRunnable runnable = new TestRunnable(); 65 | 66 | m_time = System.nanoTime(); 67 | m_executor[0].execute( runnable ); 68 | 69 | try { m_semDone.acquire(1); } 70 | catch (InterruptedException ex) { System.out.println(ex); } 71 | 72 | for (int idx=0; idx<2; idx++) 73 | { 74 | m_executor[idx].shutdown(); 75 | try 76 | { 77 | if (!m_executor[idx].awaitTermination(2, TimeUnit.SECONDS)) 78 | m_executor[idx].shutdownNow(); 79 | m_executor[idx].awaitTermination( 2, TimeUnit.SECONDS ); 80 | } 81 | catch (InterruptedException ex) 82 | { 83 | ex.printStackTrace(); 84 | } 85 | } 86 | } 87 | } 88 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | JS-Collider 2 | =========== 3 | 4 | +-----+ 5 | /-----| |-----\ +---+ 6 | / | | <=======|A/C| Session emitters 7 | / /---| |---\ \ +---+ (acceptor/connector) 8 | / / +-----+ \ \ 9 | | | <---- | | 10 | TCP/IP ---+-S | | | 11 | session +-----+ +-----+ 12 | | | | | 13 | | | | | 14 | | | | | 15 | +-----+ +-----+ 16 | | | | | 17 | | | ----> | | 18 | \ \ +-----+ / / 19 | \ \---| |---/ / 20 | \ | | S / 21 | \-----| |--+--/ 22 | +-----+ | 23 | | 24 | TCP/IP 25 | session 26 | 27 | [![Join the chat at https://gitter.im/js-labs/js-collider](https://badges.gitter.im/Join%20Chat.svg)](https://gitter.im/js-labs/js-collider?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge) 28 | 29 | JS-Collider is an asynchronous event-driven Java network (NIO) 30 | application framework designed to provide maximum performance 31 | and scalability for applications having not too many connections 32 | but significant amount of network traffic (both incoming and outgoing). 33 | 34 | Performance is achieved by specially designed threading model 35 | and lock-free algorithms ([learn more](https://github.com/js-labs/js-collider/wiki/Performance%20benchmarks)) 36 | 37 | ### Main features: 38 | 39 | * simple and flexible API ([learn more](https://github.com/js-labs/js-collider/wiki/API)) 40 | * UDP (with multicast) support 41 | * shared memory IPC support out-of-the-box ([learn more](https://github.com/js-labs/js-collider/wiki/Shared%20Memory%20IPC)) 42 | * no GC overhead on income data, only one allocation per output message 43 | * plain Java 1.7 (no any unsafe cheating) 44 | 45 | Refer the [Wiki](https://github.com/js-labs/js-collider/wiki) 46 | for API documentation and performance tests results. 47 | 48 | ### Downloading from the Maven central repository 49 | 50 | Add the following dependency section to your pom.xml: 51 | 52 | 53 | ... 54 | 55 | org.js-labs 56 | js-collider 57 | A.B.C 58 | compile 59 | 60 | ... 61 | 62 | 63 | ### Building 64 | 65 | You will require JDK 1.7 and appache ant or maven. 66 | 67 | ant package 68 | 69 | or 70 | 71 | mvn package 72 | 73 | ### Running tests 74 | 75 | ant tests 76 | 77 | ### Contacts 78 | 79 | Need more features or support? Contact info@js-labs.org 80 | -------------------------------------------------------------------------------- /tests/src/org/jsl/tests/queue_socket_send/Receiver.java: -------------------------------------------------------------------------------- 1 | package org.jsl.tests.queue_socket_send; 2 | 3 | import java.io.IOException; 4 | import java.net.SocketAddress; 5 | import java.nio.ByteBuffer; 6 | import java.nio.channels.SocketChannel; 7 | import org.jsl.tests.StreamDefragger; 8 | import org.jsl.tests.Util; 9 | 10 | 11 | public class Receiver extends Thread 12 | { 13 | private final SocketAddress m_addr; 14 | private final int m_socketBufferSize; 15 | 16 | public Receiver( SocketAddress addr, int socketBufferSize ) 17 | { 18 | m_addr = addr; 19 | m_socketBufferSize = socketBufferSize; 20 | 21 | } 22 | 23 | public void run() 24 | { 25 | try 26 | { 27 | SocketChannel socketChannel = SocketChannel.open(); 28 | socketChannel.socket().setReceiveBufferSize( m_socketBufferSize ); 29 | 30 | StreamDefragger streamDefragger = new StreamDefragger(4) 31 | { 32 | protected int validateHeader( ByteBuffer header ) 33 | { 34 | return header.getInt(); 35 | } 36 | }; 37 | 38 | if (!socketChannel.connect(m_addr) || 39 | !socketChannel.finishConnect()) 40 | { 41 | System.out.println( "SocketChannel.connect() failed." ); 42 | return; 43 | } 44 | 45 | ByteBuffer bb = ByteBuffer.allocateDirect( m_socketBufferSize ); 46 | int messagesReceived = 0; 47 | int messagesExpected = 0; 48 | int bytesReceivedTotal = 0; 49 | 50 | long startTime = System.nanoTime(); 51 | readSocketLoop: for (;;) 52 | { 53 | int bytesReceived = socketChannel.read( bb ); 54 | if (bytesReceived > 0) 55 | { 56 | bb.position(0); 57 | bb.limit( bytesReceived ); 58 | bytesReceivedTotal += bytesReceived; 59 | ByteBuffer msg = streamDefragger.getNext( bb ); 60 | while (msg != null) 61 | { 62 | msg.getInt(); // skip length 63 | final int mm = msg.getInt(); 64 | if (++messagesReceived == 1) 65 | messagesExpected = mm; 66 | else 67 | assert( messagesExpected == mm ); 68 | 69 | if (messagesExpected == messagesReceived) 70 | break readSocketLoop; 71 | 72 | msg = streamDefragger.getNext(); 73 | } 74 | bb.clear(); 75 | } 76 | } 77 | long entTime = System.nanoTime(); 78 | socketChannel.close(); 79 | 80 | System.out.println( 81 | "Received " + messagesReceived + " messages (" + bytesReceivedTotal + 82 | " bytes) at " + Util.formatDelay(startTime, entTime) + "." ); 83 | } 84 | catch (IOException ex) 85 | { 86 | ex.printStackTrace(); 87 | } 88 | } 89 | } 90 | -------------------------------------------------------------------------------- /src/main/java/org/jsl/collider/PerfCounter.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2013 Sergey Zubarev, info@js-labs.org 3 | * 4 | * This file is a part of JS-Collider framework. 5 | * 6 | * This program is free software: you can redistribute it and/or modify 7 | * it under the terms of the GNU Affero General Public License as 8 | * published by the Free Software Foundation, either version 3 of the 9 | * License, or (at your option) any later version. 10 | * 11 | * This program is distributed in the hope that it will be useful, 12 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 13 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 | * GNU Affero General Public License for more details. 15 | * 16 | * You should have received a copy of the GNU Affero General Public License 17 | * along with this program. If not, see . 18 | */ 19 | 20 | package org.jsl.collider; 21 | 22 | import java.util.concurrent.atomic.AtomicLongArray; 23 | 24 | public class PerfCounter 25 | { 26 | /* 0 - min 27 | * 1 - avg 28 | * 2 - max 29 | * 3 - sum 30 | */ 31 | private final String m_name; 32 | private final AtomicLongArray m_data; 33 | 34 | public PerfCounter( String name ) 35 | { 36 | m_name = name; 37 | m_data = new AtomicLongArray(4); 38 | m_data.set( 0, Long.MAX_VALUE ); 39 | } 40 | 41 | public final void trace( long startTime ) 42 | { 43 | final long delay = (System.nanoTime() - startTime); 44 | 45 | for (;;) 46 | { 47 | long min = m_data.get(0); 48 | if (delay >= min) 49 | break; 50 | if (m_data.compareAndSet(0, min, delay)) 51 | break; 52 | } 53 | 54 | for (;;) 55 | { 56 | final long avg = m_data.get(1); 57 | long cnt = (avg >> 32); 58 | long newAvg = cnt * (avg & 0xFFFFFFFFL) + delay; 59 | cnt++; 60 | newAvg /= cnt; 61 | newAvg &= 0xFFFFFFFFL; 62 | newAvg |= (cnt << 32); 63 | if (m_data.compareAndSet(1, avg, newAvg)) 64 | break; 65 | } 66 | 67 | for (;;) 68 | { 69 | long max = m_data.get(2); 70 | if (delay <= max) 71 | break; 72 | if (m_data.compareAndSet(2, max, delay)) 73 | break; 74 | } 75 | 76 | for (;;) 77 | { 78 | long total = m_data.get(3); 79 | if (m_data.compareAndSet(3, total, total+delay)) 80 | break; 81 | } 82 | } 83 | 84 | public final String getStats() 85 | { 86 | String str = m_name; 87 | str += ": min=" + formatUsec( m_data.get(0)/1000 ); 88 | str += " avg=" + formatUsec( (m_data.get(1) & 0xFFFFFFFFL) / 1000 ); 89 | str += " max=" + formatUsec( m_data.get(2)/1000 ); 90 | str += " sum=" + formatUsec( m_data.get(3)/1000 ); 91 | str += " cnt=" + (m_data.get(1) >> 32); 92 | return str; 93 | } 94 | 95 | private String formatUsec( long usec ) 96 | { 97 | if (usec == 0) 98 | return "0.0"; 99 | long sec = (usec / 1000000); 100 | usec %= 1000000; 101 | return String.format( "%d.%06d", sec, usec ); 102 | } 103 | } 104 | -------------------------------------------------------------------------------- /src/main/java/org/jsl/collider/ObjectCache.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2013 Sergey Zubarev, info@js-labs.org 3 | * 4 | * This file is a part of JS-Collider framework. 5 | * 6 | * This program is free software: you can redistribute it and/or modify 7 | * it under the terms of the GNU Affero General Public License as 8 | * published by the Free Software Foundation, either version 3 of the 9 | * License, or (at your option) any later version. 10 | * 11 | * This program is distributed in the hope that it will be useful, 12 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 13 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 | * GNU Affero General Public License for more details. 15 | * 16 | * You should have received a copy of the GNU Affero General Public License 17 | * along with this program. If not, see . 18 | */ 19 | 20 | package org.jsl.collider; 21 | 22 | import java.util.concurrent.locks.ReentrantLock; 23 | import java.util.logging.Level; 24 | import java.util.logging.Logger; 25 | 26 | public abstract class ObjectCache 27 | { 28 | private final String m_name; 29 | private final ReentrantLock m_lock; 30 | private final TYPE [] m_cache; 31 | private int m_size; 32 | private int m_gets; 33 | private int m_puts; 34 | private int m_miss; 35 | 36 | protected abstract TYPE allocateObject(); 37 | 38 | public ObjectCache(String name, TYPE [] cache) 39 | { 40 | m_name = name; 41 | m_lock = new ReentrantLock(); 42 | m_cache = cache; 43 | } 44 | 45 | public final boolean put(TYPE obj) 46 | { 47 | m_lock.lock(); 48 | try 49 | { 50 | m_puts++; 51 | if (m_size < m_cache.length) 52 | { 53 | final int idx = m_size++; 54 | assert( m_cache[idx] == null ); 55 | m_cache[idx] = obj; 56 | return true; 57 | } 58 | } 59 | finally 60 | { 61 | m_lock.unlock(); 62 | } 63 | return false; 64 | } 65 | 66 | public final TYPE get() 67 | { 68 | m_lock.lock(); 69 | try 70 | { 71 | m_gets++; 72 | if (m_size > 0) 73 | { 74 | final int idx = --m_size; 75 | assert(m_cache[idx] != null); 76 | TYPE ret = m_cache[idx]; 77 | m_cache[idx] = null; 78 | return ret; 79 | } 80 | m_miss++; 81 | } 82 | finally 83 | { 84 | m_lock.unlock(); 85 | } 86 | return allocateObject(); 87 | } 88 | 89 | public void clear(Logger logger) 90 | { 91 | for (int idx=0; idx. 18 | */ 19 | 20 | package org.jsl.tests.dgram_listener; 21 | 22 | import java.io.IOException; 23 | import java.nio.ByteBuffer; 24 | import java.nio.channels.DatagramChannel; 25 | import java.util.Random; 26 | 27 | public class Sender extends Thread 28 | { 29 | private final int m_messageLength; 30 | private final DatagramChannel[] m_channels; 31 | private final Random m_random; 32 | private volatile boolean m_run; 33 | 34 | public Sender( int messageLength, DatagramChannel [] channels ) 35 | { 36 | m_messageLength = messageLength; 37 | m_channels = channels; 38 | m_random = new Random( System.nanoTime() ); 39 | m_run = true; 40 | } 41 | 42 | public void run() 43 | { 44 | final ByteBuffer buf = ByteBuffer.allocateDirect( m_messageLength ); 45 | buf.putInt( 0, m_messageLength ); 46 | for (int idx=4; idx 0) 67 | { 68 | batchMsgs--; 69 | } 70 | else 71 | { 72 | final int random = m_random.nextInt( 100 ); 73 | if (random < 20) 74 | { 75 | batchMsgs = m_random.nextInt( 1000 ); 76 | System.out.println( 77 | "Batch: [" + msgs + ":" + (msgs+batchMsgs) + ", " + batchMsgs + "]" ); 78 | } 79 | else 80 | Thread.sleep( 100 ); 81 | } 82 | } 83 | } 84 | catch (IOException ex) 85 | { 86 | ex.printStackTrace(); 87 | } 88 | catch (InterruptedException ex) 89 | { 90 | ex.printStackTrace(); 91 | } 92 | } 93 | 94 | public void stopAndWait() 95 | { 96 | m_run = false; 97 | try 98 | { 99 | join(); 100 | } 101 | catch (InterruptedException ex) 102 | { 103 | ex.printStackTrace(); 104 | } 105 | } 106 | } 107 | -------------------------------------------------------------------------------- /src/main/java/org/jsl/collider/Util.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2013 Sergey Zubarev, info@js-labs.org 3 | * 4 | * This file is a part of JS-Collider framework. 5 | * 6 | * This program is free software: you can redistribute it and/or modify 7 | * it under the terms of the GNU General Public License as published by 8 | * the Free Software Foundation, either version 3 of the License, or 9 | * (at your option) any later version. 10 | * This program is distributed in the hope that it will be useful, 11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. 13 | * See the GNU General Public License for more details. 14 | * You should have received a copy of the GNU General Public License 15 | * along with this program. If not, see . 16 | */ 17 | 18 | package org.jsl.collider; 19 | 20 | import java.nio.ByteBuffer; 21 | 22 | public class Util 23 | { 24 | private static final char [] HD = {'0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B', 'C', 'D', 'E', 'F'}; 25 | 26 | public static String formatDelay(long startTime, long endTime) 27 | { 28 | final long delay = ((endTime - startTime) / 1000); 29 | if (delay > 0) 30 | return String.format("%d.%06d", delay/1000000, delay%1000000); 31 | return "0.0"; 32 | } 33 | 34 | public static void hexDump(ByteBuffer byteBuffer, StringBuilder sb, int maxLines) 35 | { 36 | int pos = byteBuffer.position(); 37 | int limit = byteBuffer.limit(); 38 | if (pos == limit) 39 | { 40 | sb.append(""); 41 | return; 42 | } 43 | 44 | int c = ((limit - pos) / 16); 45 | if (c > maxLines) 46 | limit = (pos + (maxLines * 16)); 47 | 48 | /* "0000: 00 01 02 03 04 05 06 07 08 09 0A 0B 0C 0D 0E 0F | ................" */ 49 | sb.append(" 0 1 2 3 4 5 6 7 8 9 A B C D E F ["); 50 | sb.append(pos); 51 | sb.append(", "); 52 | sb.append(limit); 53 | sb.append("]\n"); 54 | 55 | while (pos < limit) 56 | { 57 | int p = (pos - byteBuffer.position()); 58 | sb.append(HD[(p >> 12) & 0x0F]); 59 | sb.append(HD[(p >> 8) & 0x0F]); 60 | sb.append(HD[(p >> 4) & 0x0F]); 61 | sb.append(HD[p & 0x0F]); 62 | sb.append(": "); 63 | 64 | int n = Math.min((limit - pos), 16); 65 | p = pos; 66 | for (c=n; c>0; c--, p++) 67 | { 68 | final int v = (((int)byteBuffer.get(p)) & 0xFF); 69 | sb.append(HD[(v >> 4)]); 70 | sb.append(HD[v & 0x0F]); 71 | sb.append(' '); 72 | } 73 | 74 | for (c=(16-n); c>0; c--) 75 | sb.append(" "); 76 | 77 | sb.append("| "); 78 | 79 | p = pos; 80 | for (c=n; c>0; c--, p++) 81 | { 82 | final int v = byteBuffer.get(p); 83 | final char vc = ((v >= 32) ? (char)v : '.'); 84 | sb.append(vc); 85 | } 86 | 87 | sb.append('\n'); 88 | pos += n; 89 | } 90 | } 91 | 92 | public static String hexDump(ByteBuffer byteBuffer, int maxLines) 93 | { 94 | final StringBuilder sb = new StringBuilder(); 95 | hexDump(byteBuffer, sb, maxLines); 96 | return sb.toString(); 97 | } 98 | 99 | public static String hexDump(ByteBuffer byteBuffer) 100 | { 101 | return hexDump(byteBuffer, 10); 102 | } 103 | 104 | public static String hexDump(RetainableByteBuffer bb) 105 | { 106 | return hexDump(bb.getNioByteBuffer()); 107 | } 108 | } 109 | -------------------------------------------------------------------------------- /src/main/java/org/jsl/collider/RetainableByteBufferImpl.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2013 Sergey Zubarev, info@js-labs.org 3 | * 4 | * This file is a part of JS-Collider framework. 5 | * 6 | * This program is free software: you can redistribute it and/or modify 7 | * it under the terms of the GNU Affero General Public License as 8 | * published by the Free Software Foundation, either version 3 of the 9 | * License, or (at your option) any later version. 10 | * 11 | * This program is distributed in the hope that it will be useful, 12 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 13 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 | * GNU Affero General Public License for more details. 15 | * 16 | * You should have received a copy of the GNU Affero General Public License 17 | * along with this program. If not, see . 18 | */ 19 | package org.jsl.collider; 20 | 21 | import java.nio.ByteBuffer; 22 | 23 | abstract class RetainableByteBufferImpl extends RetainableByteBuffer 24 | { 25 | public RetainableByteBufferImpl( ByteBuffer byteBuffer ) 26 | { 27 | super( byteBuffer ); 28 | } 29 | 30 | /* 31 | * NIO Buffer 32 | */ 33 | 34 | public int capacity() 35 | { 36 | return m_buf.capacity(); 37 | } 38 | 39 | public RetainableByteBuffer clear() 40 | { 41 | m_buf.clear(); 42 | return this; 43 | } 44 | 45 | public RetainableByteBuffer flip() 46 | { 47 | m_buf.flip(); 48 | return this; 49 | } 50 | 51 | public int limit() 52 | { 53 | return m_buf.limit(); 54 | } 55 | 56 | public RetainableByteBuffer limit( int limit ) 57 | { 58 | m_buf.limit( limit ); 59 | return this; 60 | } 61 | 62 | public int position() 63 | { 64 | return m_buf.position(); 65 | } 66 | 67 | public RetainableByteBuffer position( int position ) 68 | { 69 | m_buf.position( position ); 70 | return this; 71 | } 72 | 73 | public RetainableByteBuffer rewind() 74 | { 75 | m_buf.rewind(); 76 | return this; 77 | } 78 | 79 | /* 80 | * NIO ByteBuffer 81 | */ 82 | 83 | public byte get(int index) 84 | { 85 | return m_buf.get(index); 86 | } 87 | 88 | public RetainableByteBuffer put(int index, byte value) 89 | { 90 | m_buf.put( index, value ); 91 | return this; 92 | } 93 | 94 | public short getShort(int index) 95 | { 96 | return m_buf.getShort(index); 97 | } 98 | 99 | public RetainableByteBuffer putShort(int index , short value) 100 | { 101 | m_buf.putShort(index, value); 102 | return this; 103 | } 104 | 105 | public int getInt(int index) 106 | { 107 | return m_buf.getInt(index); 108 | } 109 | 110 | public RetainableByteBuffer putInt(int index, int value) 111 | { 112 | m_buf.putInt(index, value); 113 | return this; 114 | } 115 | 116 | public long getLong(int index) 117 | { 118 | return m_buf.getLong(index); 119 | } 120 | 121 | public RetainableByteBuffer putLong(int index, long value) 122 | { 123 | m_buf.putLong(index, value); 124 | return this; 125 | } 126 | 127 | public float getFloat(int index) 128 | { 129 | return m_buf.getFloat(index); 130 | } 131 | 132 | public RetainableByteBuffer putFloat(int index, float value) 133 | { 134 | m_buf.putFloat(index, value); 135 | return this; 136 | } 137 | 138 | public double getDouble(int index) 139 | { 140 | return m_buf.getDouble(index); 141 | } 142 | 143 | public RetainableByteBuffer putDouble(int index, double value) 144 | { 145 | m_buf.putDouble(index, value); 146 | return this; 147 | } 148 | } 149 | -------------------------------------------------------------------------------- /tests/src/org/jsl/tests/sched_latency/SL_Cond.java: -------------------------------------------------------------------------------- 1 | /* 2 | * JS-Collider framework. 3 | * Copyright (C) 2013 Sergey Zubarev 4 | * info@js-labs.org 5 | * 6 | * This program is free software: you can redistribute it and/or modify 7 | * it under the terms of the GNU Affero General Public License as 8 | * published by the Free Software Foundation, either version 3 of the 9 | * License, or (at your option) any later version. 10 | * 11 | * This program is distributed in the hope that it will be useful, 12 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 13 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 | * GNU Affero General Public License for more details. 15 | * 16 | * You should have received a copy of the GNU Affero General Public License 17 | * along with this program. If not, see . 18 | */ 19 | 20 | package org.jsl.tests.sched_latency; 21 | 22 | import java.util.concurrent.Semaphore; 23 | import java.util.concurrent.locks.Condition; 24 | import java.util.concurrent.locks.ReentrantLock; 25 | 26 | public class SL_Cond 27 | { 28 | private static class Event 29 | { 30 | private final ReentrantLock m_lock; 31 | private final Condition m_cond; 32 | private boolean m_signal; 33 | 34 | public Event() 35 | { 36 | m_lock = new ReentrantLock(); 37 | m_cond = m_lock.newCondition(); 38 | m_signal = false; 39 | } 40 | 41 | public void signal() 42 | { 43 | m_lock.lock(); 44 | try 45 | { 46 | m_signal = true; 47 | m_cond.signal(); 48 | } 49 | finally 50 | { 51 | m_lock.unlock(); 52 | } 53 | } 54 | 55 | public void await() 56 | { 57 | m_lock.lock(); 58 | try 59 | { 60 | while (!m_signal) 61 | m_cond.await(); 62 | m_signal = false; 63 | } 64 | catch (InterruptedException ex) 65 | { 66 | ex.printStackTrace(); 67 | } 68 | finally 69 | { 70 | m_lock.unlock(); 71 | } 72 | } 73 | } 74 | 75 | private class TestThread implements Runnable 76 | { 77 | private final Event m_evt1; 78 | private final Event m_evt2; 79 | 80 | public TestThread( Event evt1, Event evt2 ) 81 | { 82 | m_evt1 = evt1; 83 | m_evt2 = evt2; 84 | } 85 | 86 | public void run() 87 | { 88 | for (;;) 89 | { 90 | m_evt1.await(); 91 | 92 | if (m_idx == m_res.length) 93 | break; 94 | 95 | long time = System.nanoTime(); 96 | m_res[m_idx] = (time - m_time); 97 | m_idx++; 98 | m_time = time; 99 | 100 | m_evt2.signal(); 101 | } 102 | 103 | m_evt2.signal(); 104 | m_semDone.release(); 105 | } 106 | } 107 | 108 | private final long [] m_res; 109 | private final Semaphore m_semDone; 110 | private long m_time; 111 | private int m_idx; 112 | 113 | public SL_Cond( long [] res ) 114 | { 115 | m_res = res; 116 | m_semDone = new Semaphore(0); 117 | } 118 | 119 | public void start() 120 | { 121 | Event evt1 = new Event(); 122 | Event evt2 = new Event(); 123 | 124 | new Thread(new TestThread(evt1, evt2)).start(); 125 | new Thread(new TestThread(evt2, evt1)).start(); 126 | 127 | m_time = System.nanoTime(); 128 | evt1.signal(); 129 | 130 | try { m_semDone.acquire(2); } 131 | catch (InterruptedException ex) { System.out.println(ex); } 132 | } 133 | } 134 | -------------------------------------------------------------------------------- /src/main/java/org/jsl/collider/ShMemClient.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2013 Sergey Zubarev, info@js-labs.org 3 | * 4 | * This file is a part of JS-Collider framework. 5 | * 6 | * This program is free software: you can redistribute it and/or modify 7 | * it under the terms of the GNU Affero General Public License as 8 | * published by the Free Software Foundation, either version 3 of the 9 | * License, or (at your option) any later version. 10 | * 11 | * This program is distributed in the hope that it will be useful, 12 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 13 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 | * GNU Affero General Public License for more details. 15 | * 16 | * You should have received a copy of the GNU Affero General Public License 17 | * along with this program. If not, see . 18 | */ 19 | 20 | package org.jsl.collider; 21 | 22 | import java.io.File; 23 | import java.io.IOException; 24 | import java.nio.BufferOverflowException; 25 | import java.nio.ByteBuffer; 26 | import java.nio.CharBuffer; 27 | import java.nio.charset.Charset; 28 | import java.nio.charset.CharsetEncoder; 29 | 30 | public class ShMemClient extends ShMem 31 | { 32 | private final int m_blockSize; 33 | private final ChannelIn m_in; 34 | private final ChannelOut m_out; 35 | private final ByteBuffer m_c2sBB; 36 | private final ByteBuffer m_s2cBB; 37 | 38 | public ShMemClient( String fileHint, int blockSize, File directory ) throws IOException 39 | { 40 | /* It will be better if block size is a multiplier of 4096. */ 41 | if ((blockSize & 0x0FFF) > 0) 42 | { 43 | blockSize &= ~0x0FFF; 44 | blockSize += 0x1000; 45 | } 46 | m_blockSize = blockSize; 47 | 48 | final String prefix = "jsc-" + fileHint + "-"; 49 | final File fileC2S = File.createTempFile( prefix, ".c2s", directory ); 50 | m_out = new ChannelOut( fileC2S, blockSize, true ); 51 | 52 | final File fileS2C = File.createTempFile( prefix, ".s2c", directory ); 53 | m_in = new ChannelIn( fileS2C, blockSize, true ); 54 | 55 | final CharsetEncoder encoder = Charset.defaultCharset().newEncoder(); 56 | m_c2sBB = encoder.encode( CharBuffer.wrap(fileC2S.getAbsolutePath()) ); 57 | m_s2cBB = encoder.encode( CharBuffer.wrap(fileS2C.getAbsolutePath()) ); 58 | } 59 | 60 | public ShMemClient( String fileHint, int blockSize ) throws IOException 61 | { 62 | this( fileHint, blockSize, null ); 63 | } 64 | 65 | public ShMemClient( String fileHint ) throws IOException 66 | { 67 | this( fileHint, 64*1024 ); 68 | } 69 | 70 | public final int getDescriptorLength() 71 | { 72 | /* Shared memory session descriptor structure: 73 | * short : descriptor version 74 | * int : shared memory block size 75 | * short : length of the (client->server) file path 76 | * : (client->server) file absolute path 77 | * short : length of the (server->client) file name 78 | * : (server->client) file absolute path 79 | */ 80 | return (2 + 81 | 4 + 82 | 2 + m_c2sBB.remaining() + 83 | 2 + m_s2cBB.remaining()); 84 | } 85 | 86 | public final void getDescriptor( ByteBuffer buf ) throws BufferOverflowException 87 | { 88 | assert( buf.remaining() >= getDescriptorLength() ); 89 | buf.putShort( (short) 1 ); 90 | buf.putInt( m_blockSize ); 91 | buf.putShort( (short) m_c2sBB.remaining() ); 92 | buf.put( m_c2sBB ); 93 | buf.putShort( (short) m_s2cBB.remaining() ); 94 | buf.put( m_s2cBB ); 95 | } 96 | 97 | public ChannelIn getIn() 98 | { 99 | return m_in; 100 | } 101 | 102 | public ChannelOut getOut() 103 | { 104 | return m_out; 105 | } 106 | 107 | public void close() 108 | { 109 | m_in.close(); 110 | m_out.close(); 111 | } 112 | } 113 | -------------------------------------------------------------------------------- /tests/src/org/jsl/tests/thread_pool_throughput/ExecutorTest.java: -------------------------------------------------------------------------------- 1 | /* 2 | * JS-Collider framework. 3 | * Copyright (C) 2013 Sergey Zubarev 4 | * info@js-labs.org 5 | * 6 | * This program is free software: you can redistribute it and/or modify 7 | * it under the terms of the GNU Affero General Public License as 8 | * published by the Free Software Foundation, either version 3 of the 9 | * License, or (at your option) any later version. 10 | * 11 | * This program is distributed in the hope that it will be useful, 12 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 13 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 | * GNU Affero General Public License for more details. 15 | * 16 | * You should have received a copy of the GNU Affero General Public License 17 | * along with this program. If not, see . 18 | */ 19 | 20 | package org.jsl.tests.thread_pool_throughput; 21 | 22 | import org.jsl.tests.Util; 23 | import java.util.concurrent.ExecutorService; 24 | import java.util.concurrent.Executors; 25 | import java.util.concurrent.Semaphore; 26 | import java.util.concurrent.TimeUnit; 27 | import java.util.concurrent.atomic.AtomicInteger; 28 | 29 | public class ExecutorTest extends Test 30 | { 31 | private final AtomicInteger m_state; 32 | private final ExecutorService m_executor; 33 | private long m_startTime; 34 | private long m_endTime; 35 | 36 | private class TestRunnable implements Runnable 37 | { 38 | private int m_value; 39 | 40 | public TestRunnable( int value ) 41 | { 42 | m_value = value; 43 | } 44 | 45 | public void run() 46 | { 47 | if (m_value == 0) 48 | { 49 | int state = m_state.incrementAndGet(); 50 | if (state == 1) 51 | m_startTime = System.nanoTime(); 52 | } 53 | else if (m_value == Integer.MIN_VALUE) 54 | { 55 | int state = m_state.incrementAndGet(); 56 | if (state == (m_producers*2)) 57 | m_endTime = System.nanoTime(); 58 | } 59 | } 60 | } 61 | 62 | private class Producer implements Runnable 63 | { 64 | public void run() 65 | { 66 | int events = (m_totalEvents / m_producers); 67 | TestRunnable [] runnable = new TestRunnable[events]; 68 | for (int idx=0; idx. 18 | */ 19 | 20 | package org.jsl.tests.queue_socket_send; 21 | 22 | import org.jsl.tests.Util; 23 | 24 | import java.io.IOException; 25 | import java.net.InetSocketAddress; 26 | import java.net.SocketAddress; 27 | import java.nio.ByteBuffer; 28 | import java.nio.channels.ServerSocketChannel; 29 | import java.nio.channels.SocketChannel; 30 | 31 | public abstract class Sender 32 | { 33 | private final String m_name; 34 | protected final int m_sessions; 35 | protected final int m_messages; 36 | protected final int m_messageLength; 37 | protected final int m_socketBufferSize; 38 | 39 | protected Sender( String name, 40 | int sessions, 41 | int messages, 42 | int messageLength, 43 | int socketBufferSize ) 44 | { 45 | m_name = name; 46 | m_sessions = sessions; 47 | m_messages = messages; 48 | m_messageLength = ((messageLength < 8) ? 8 : messageLength); 49 | m_socketBufferSize = socketBufferSize; 50 | } 51 | 52 | protected void run( SessionFactory sessionFactory ) 53 | { 54 | try 55 | { 56 | InetSocketAddress addr = new InetSocketAddress( "localhost", 0 ); 57 | ServerSocketChannel serverSocketChannel = ServerSocketChannel.open(); 58 | serverSocketChannel.socket().bind( addr ); 59 | 60 | Receiver [] receiver = new Receiver[m_sessions]; 61 | SocketAddress localAddr = serverSocketChannel.socket().getLocalSocketAddress(); 62 | for (int idx=0; idx. 18 | */ 19 | 20 | package org.jsl.tests.msg_size_eq_block_size; 21 | 22 | import java.io.IOException; 23 | import java.net.InetSocketAddress; 24 | import java.net.Socket; 25 | import java.net.SocketAddress; 26 | import java.nio.ByteBuffer; 27 | import java.nio.channels.SocketChannel; 28 | 29 | public class Client 30 | { 31 | private final InetSocketAddress m_addr; 32 | private final int m_testId; 33 | private final int m_messageLength; 34 | private final Thread m_thread; 35 | 36 | private class ClientThread extends Thread 37 | { 38 | public void run() 39 | { 40 | try 41 | { 42 | final int messageLength = m_messageLength; 43 | final SocketChannel socketChannel = SocketChannel.open(m_addr); 44 | final Socket socket = socketChannel.socket(); 45 | socket.setTcpNoDelay(true); 46 | 47 | final SocketAddress remoteAddr = socket.getRemoteSocketAddress(); 48 | System.out.println( 49 | "Client " + m_testId + ": " + socket.getLocalSocketAddress() + 50 | " -> " + remoteAddr + ": connected"); 51 | 52 | final ByteBuffer bb = ByteBuffer.allocateDirect(messageLength); 53 | bb.putInt(0, messageLength); 54 | for (int idx=4; idx " + remoteAddr + ": connection closed"); 91 | } 92 | catch (final IOException ex) 93 | { 94 | ex.printStackTrace(); 95 | } 96 | } 97 | } 98 | 99 | public Client(InetSocketAddress addr, int testId, int messageLength) 100 | { 101 | m_addr = addr; 102 | m_testId = testId; 103 | m_messageLength = messageLength; 104 | m_thread = new ClientThread(); 105 | m_thread.start(); 106 | } 107 | 108 | public void stopAndWait() 109 | { 110 | try 111 | { 112 | m_thread.join(); 113 | } 114 | catch (final InterruptedException ex) 115 | { 116 | ex.printStackTrace(); 117 | } 118 | } 119 | } 120 | -------------------------------------------------------------------------------- /tests/src/org/jsl/tests/session_latency/Client.java: -------------------------------------------------------------------------------- 1 | /* 2 | * JS-Collider framework tests. 3 | * Copyright (C) 2013 Sergey Zubarev 4 | * info@js-labs.org 5 | * 6 | * This program is free software: you can redistribute it and/or modify 7 | * it under the terms of the GNU Affero General Public License as 8 | * published by the Free Software Foundation, either version 3 of the 9 | * License, or (at your option) any later version. 10 | * 11 | * This program is distributed in the hope that it will be useful, 12 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 13 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 | * GNU Affero General Public License for more details. 15 | * 16 | * You should have received a copy of the GNU Affero General Public License 17 | * along with this program. If not, see . 18 | */ 19 | 20 | package org.jsl.tests.session_latency; 21 | 22 | import org.jsl.collider.StatCounter; 23 | import org.jsl.tests.Util; 24 | import java.io.IOException; 25 | import java.net.InetAddress; 26 | import java.net.InetSocketAddress; 27 | import java.net.Socket; 28 | import java.net.UnknownHostException; 29 | import java.nio.ByteBuffer; 30 | import java.nio.channels.SocketChannel; 31 | 32 | public class Client 33 | { 34 | private InetSocketAddress m_addr; 35 | private Thread [] m_threads; 36 | 37 | private class ClientThread extends Thread 38 | { 39 | public void run() 40 | { 41 | try 42 | { 43 | final SocketChannel socketChannel = SocketChannel.open( m_addr ); 44 | final Socket socket = socketChannel.socket(); 45 | socket.setTcpNoDelay( true ); 46 | System.out.println( 47 | "Client connected " + socket.getLocalSocketAddress() + 48 | " -> " + socket.getRemoteSocketAddress() + "." ); 49 | 50 | final StatCounter statCounter = new StatCounter( socket.getLocalSocketAddress() + " latency" ); 51 | final ByteBuffer bb = ByteBuffer.allocateDirect( 1024*16 ); 52 | int bytesReceived = socketChannel.read( bb ); 53 | long recvTime = System.nanoTime(); 54 | int messages = 1; 55 | bb.flip(); 56 | int messageLength = bb.getInt(0); 57 | assert( bytesReceived == messageLength ); 58 | 59 | for (;;) 60 | { 61 | final long sendTime = bb.getLong(4); 62 | 63 | /* First message we receive is a server start message */ 64 | if (messages > 10) 65 | statCounter.trace( (recvTime - sendTime) / 1000 ); 66 | 67 | bb.putLong( 4, System.nanoTime() ); 68 | final int bytesSent = socketChannel.write( bb ); 69 | assert( bytesSent == messageLength ); 70 | 71 | bb.clear(); 72 | bytesReceived = socketChannel.read( bb ); 73 | recvTime = System.nanoTime(); 74 | messages++; 75 | 76 | if (bytesReceived == 4) 77 | break; 78 | 79 | bb.flip(); 80 | messageLength = bb.getInt(0); 81 | assert( messageLength == bytesReceived ); 82 | } 83 | socketChannel.close(); 84 | 85 | System.out.println( statCounter.getStats() ); 86 | } 87 | catch (final IOException ex) 88 | { 89 | ex.printStackTrace(); 90 | } 91 | } 92 | } 93 | 94 | public Client( int sessions ) 95 | { 96 | m_threads = new Thread[sessions]; 97 | for (int idx=0; idx. 18 | */ 19 | 20 | package org.jsl.tests.pubsub; 21 | 22 | import java.io.IOException; 23 | import java.net.InetSocketAddress; 24 | 25 | import org.jsl.collider.*; 26 | 27 | import java.nio.ByteBuffer; 28 | 29 | public class SubClient extends Thread 30 | { 31 | private final Main m_main; 32 | private final InetSocketAddress m_addr; 33 | private final int m_socketBufferSize; 34 | 35 | private class SessionListener implements Session.Listener 36 | { 37 | private final Session m_session; 38 | private final StreamDefragger m_stream; 39 | private int m_messages; 40 | private int m_messagesReceived; 41 | 42 | public SessionListener( Session session ) 43 | { 44 | System.out.println( 45 | "SubClient connected " + session.getLocalAddress() + 46 | " -> " + session.getRemoteAddress() + "." ); 47 | 48 | m_session = session; 49 | m_stream = new StreamDefragger(4) 50 | { 51 | public int validateHeader( ByteBuffer header ) 52 | { 53 | return header.getInt(); 54 | } 55 | }; 56 | 57 | final ByteBuffer buf = ByteBuffer.allocateDirect( 5 ); 58 | buf.putInt(5); 59 | buf.put( (byte) 1 /*subscriber*/); 60 | buf.position(0); 61 | session.sendData( buf ); 62 | } 63 | 64 | public void onDataReceived( RetainableByteBuffer data ) 65 | { 66 | if (data.remaining() == 0) 67 | throw new AssertionError(); 68 | 69 | RetainableByteBuffer msg = m_stream.getNext( data ); 70 | while (msg != null) 71 | { 72 | final int messagesReceived = ++m_messagesReceived; 73 | //System.out.println( "msg [" + messagesReceived + "]\n" + Util.hexDump(msg) ); 74 | if (m_messages == 0) 75 | { 76 | msg.getInt(); // skip message length 77 | m_messages = msg.getInt(); 78 | } 79 | 80 | if (messagesReceived == m_messages) 81 | { 82 | m_main.onSubscriberDone(); 83 | m_session.getCollider().stop(); 84 | } 85 | 86 | msg = m_stream.getNext(); 87 | } 88 | } 89 | 90 | public void onConnectionClosed() 91 | { 92 | System.out.println( "SubClient: connection closed." ); 93 | } 94 | } 95 | 96 | private class SubConnector extends Connector 97 | { 98 | public SubConnector( InetSocketAddress addr ) 99 | { 100 | super( addr ); 101 | socketRecvBufSize = m_socketBufferSize; 102 | socketSendBufSize = m_socketBufferSize; 103 | } 104 | 105 | public Session.Listener createSessionListener( Session session ) 106 | { 107 | return new SessionListener( session ); 108 | } 109 | 110 | public void onException( IOException ex ) 111 | { 112 | ex.printStackTrace(); 113 | } 114 | } 115 | 116 | public SubClient( Main main, InetSocketAddress addr, int socketBufferSize ) 117 | { 118 | m_main = main; 119 | m_addr = addr; 120 | m_socketBufferSize = socketBufferSize; 121 | } 122 | 123 | public void run() 124 | { 125 | try 126 | { 127 | final Collider collider = Collider.create(); 128 | collider.addConnector( new SubConnector(m_addr) ); 129 | collider.run(); 130 | } 131 | catch (IOException ex) 132 | { 133 | ex.printStackTrace(); 134 | } 135 | } 136 | } 137 | -------------------------------------------------------------------------------- /tests/src/org/jsl/tests/thread_pool_throughput/ThreadPoolTest.java: -------------------------------------------------------------------------------- 1 | /* 2 | * JS-Collider framework. 3 | * Copyright (C) 2013 Sergey Zubarev 4 | * info@js-labs.org 5 | * 6 | * This program is free software: you can redistribute it and/or modify 7 | * it under the terms of the GNU Affero General Public License as 8 | * published by the Free Software Foundation, either version 3 of the 9 | * License, or (at your option) any later version. 10 | * 11 | * This program is distributed in the hope that it will be useful, 12 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 13 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 | * GNU Affero General Public License for more details. 15 | * 16 | * You should have received a copy of the GNU Affero General Public License 17 | * along with this program. If not, see . 18 | */ 19 | 20 | package org.jsl.tests.thread_pool_throughput; 21 | import org.jsl.collider.ThreadPool; 22 | import org.jsl.tests.Util; 23 | 24 | import java.util.concurrent.Semaphore; 25 | import java.util.concurrent.atomic.AtomicInteger; 26 | 27 | public class ThreadPoolTest extends Test 28 | { 29 | private final ThreadPool m_threadPool; 30 | private final Semaphore m_semReady; 31 | private final Semaphore m_semStart; 32 | private final Semaphore m_semDone; 33 | private final AtomicInteger m_state; 34 | private long m_startTime; 35 | private long m_endTime; 36 | 37 | private class TestRunnable extends ThreadPool.Runnable 38 | { 39 | private int m_value; 40 | 41 | public TestRunnable( int value ) 42 | { 43 | m_value = value; 44 | } 45 | 46 | public void runInThreadPool() 47 | { 48 | if (m_value == 0) 49 | { 50 | int state = m_state.incrementAndGet(); 51 | if (state == 1) 52 | m_startTime = System.nanoTime(); 53 | } 54 | else if (m_value == Integer.MIN_VALUE) 55 | { 56 | int state = m_state.incrementAndGet(); 57 | if (state == (m_producers*2)) 58 | { 59 | m_endTime = System.nanoTime(); 60 | m_semDone.release(); 61 | } 62 | } 63 | } 64 | } 65 | 66 | private class Producer implements Runnable 67 | { 68 | public void run() 69 | { 70 | int events = (m_totalEvents / m_producers); 71 | TestRunnable [] runnable = new TestRunnable[events]; 72 | for (int idx=0; idx. 18 | */ 19 | 20 | package org.jsl.collider; 21 | 22 | import java.net.SocketAddress; 23 | import java.nio.ByteBuffer; 24 | 25 | public interface Session 26 | { 27 | interface Listener 28 | { 29 | /** 30 | * Called by framework when some data is available. 31 | * Executed serially in a one thread, but not necessary always the same. 32 | * Position in the byte buffer can be greater than 0, 33 | * limit can be less than capacity. 34 | * @param data the data received from the related socket 35 | */ 36 | void onDataReceived(RetainableByteBuffer data); 37 | 38 | /** 39 | * Called by framework when underlying socket channel 40 | * is closed and all income data is processed. 41 | */ 42 | void onConnectionClosed(); 43 | } 44 | 45 | /** 46 | * @return Collider instance the session is linked with. 47 | */ 48 | Collider getCollider(); 49 | 50 | /** 51 | * @return local socket address of the session. 52 | */ 53 | SocketAddress getLocalAddress(); 54 | 55 | /** 56 | * @return remote socket address of the session. 57 | */ 58 | SocketAddress getRemoteAddress(); 59 | 60 | /** 61 | * Schedules data to be sent to the underlying socket channel. 62 | * Retains the data buffer, but buffer remains unchanged 63 | * (even it's attributes like a position, limit etc) 64 | * so the buffer can be reused to send the same data 65 | * to the different sessions. 66 | * @param data byte buffer with data to send 67 | * @return value greater than 0 if byte buffer is retained by the framework, 68 | * (data will be sent as soon as possible), or less than 0 if the session is closed. 69 | */ 70 | int sendData(ByteBuffer data); 71 | int sendData(RetainableByteBuffer data); 72 | 73 | /** 74 | * Method makes an attempt to write data synchronously to the underlying socket channel. 75 | * It can happen if it is the single thread calling the sendData or sendDataSync. 76 | * Otherwise data will be sent as sendData would be called. 77 | * @param data byte buffer with data to send 78 | * @return 0 if data has been written to the socket and byte buffer can be reused, 79 | * greater than 0 if byte buffer is retained by the framework, will be sent as soon as possible, 80 | * less than 0 if session is closed. 81 | */ 82 | int sendDataSync(ByteBuffer data); 83 | 84 | /** 85 | * Method to be used to close the session. 86 | * Works asynchronously so connection will not be closed immediately 87 | * after function return. Outgoing data scheduled but not sent yet 88 | * will be sent. Any data already read from the socket at the moment 89 | * but not processed yet will be processed. onConnectionClosed 90 | * will be called after all received data will be processed. 91 | * All further sendData, sendDataAsync and 92 | * closeConnection calls will return -1. 93 | * @return less than 0 if session already has been closed, 94 | * otherwise amount of data waiting to be sent. 95 | */ 96 | int closeConnection(); 97 | 98 | /** 99 | * Replaces the current session listener with a new one. 100 | * Supposed to be called only from the onDataReceived() callback. 101 | * Calling it not from the onDataReceived callback will result 102 | * in undefined behaviour. 103 | * @param newListener the new listener to be used for the session 104 | * @return the previous listener was used to the session 105 | */ 106 | Listener replaceListener(Listener newListener); 107 | 108 | int accelerate(ShMem shMem, ByteBuffer message); 109 | } 110 | -------------------------------------------------------------------------------- /tests/src/org/jsl/tests/msg_size_eq_block_size/Server.java: -------------------------------------------------------------------------------- 1 | /* 2 | * JS-Collider framework tests. 3 | * Copyright (C) 2022 Sergey Zubarev 4 | * info@js-labs.org 5 | * 6 | * This program is free software: you can redistribute it and/or modify 7 | * it under the terms of the GNU Affero General Public License as 8 | * published by the Free Software Foundation, either version 3 of the 9 | * License, or (at your option) any later version. 10 | * 11 | * This program is distributed in the hope that it will be useful, 12 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 13 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 | * GNU Affero General Public License for more details. 15 | * 16 | * You should have received a copy of the GNU Affero General Public License 17 | * along with this program. If not, see . 18 | */ 19 | 20 | package org.jsl.tests.msg_size_eq_block_size; 21 | 22 | import org.jsl.collider.Acceptor; 23 | import org.jsl.collider.Collider; 24 | import org.jsl.collider.RetainableByteBuffer; 25 | import org.jsl.collider.Session; 26 | import java.io.IOException; 27 | import java.net.InetSocketAddress; 28 | 29 | public class Server 30 | { 31 | private final int m_messages; 32 | private final int m_messageLength; 33 | private InetSocketAddress m_addr; 34 | private int m_testId; 35 | 36 | private class ServerListener implements Session.Listener 37 | { 38 | private final Session m_session; 39 | private final int m_testId; 40 | private int m_messages; 41 | 42 | ServerListener(Session session, int testId) 43 | { 44 | m_session = session; 45 | m_testId = testId; 46 | } 47 | 48 | public void onDataReceived(RetainableByteBuffer data) 49 | { 50 | final int pos = data.position(); 51 | final int bytesReceived = data.remaining(); 52 | final int messageLength = data.getInt(pos); 53 | 54 | if (bytesReceived != messageLength) 55 | throw new AssertionError(); 56 | 57 | // for the first test client is responsible to close connection 58 | final RetainableByteBuffer reply = data.slice(); 59 | m_session.sendData(reply); 60 | reply.release(); 61 | 62 | final int messages = ++m_messages; 63 | if (m_testId == 1) 64 | { 65 | // do nothing 66 | } 67 | else if (m_testId == 2) 68 | { 69 | // for the second test server supposed to close connection 70 | if (messages >= Server.this.m_messages) 71 | m_session.closeConnection(); 72 | } 73 | } 74 | 75 | public void onConnectionClosed() 76 | { 77 | System.out.println("Server: connection " + 78 | m_session.getLocalAddress() + " -> " + m_session.getRemoteAddress() + ": closed"); 79 | if (m_testId == 1) 80 | new Client(m_addr, /*test id*/2, m_messageLength); 81 | else if (m_testId == 2) 82 | m_session.getCollider().stop(); 83 | } 84 | } 85 | 86 | private class TestAcceptor extends Acceptor 87 | { 88 | TestAcceptor() 89 | { 90 | super(0); 91 | } 92 | 93 | public void onAcceptorStarted(Collider collider, int localPort) 94 | { 95 | System.out.println("Server started at port " + localPort); 96 | final int messageLength = collider.getConfig().inputQueueBlockSize; 97 | m_addr = new InetSocketAddress("localhost", localPort); 98 | new Client(m_addr, /*test id*/1, messageLength); 99 | } 100 | 101 | public Session.Listener createSessionListener(Session session) 102 | { 103 | final int testId = ++m_testId; 104 | System.out.println("Server: connection " + 105 | session.getLocalAddress() + " -> " + session.getRemoteAddress() + " accepted, " + testId); 106 | return new ServerListener(session, testId); 107 | } 108 | } 109 | 110 | public Server(int messages, int messageLength) 111 | { 112 | m_messages = messages; 113 | m_messageLength = messageLength; 114 | m_testId = 0; 115 | } 116 | 117 | public void run() 118 | { 119 | try 120 | { 121 | /* Would be better to avoid message fragmentation. */ 122 | final Collider.Config config = new Collider.Config(); 123 | config.inputQueueBlockSize = m_messageLength; 124 | 125 | final Collider collider = Collider.create(config); 126 | collider.addAcceptor(new TestAcceptor()); 127 | collider.run(); 128 | } 129 | catch (final IOException ex) 130 | { 131 | ex.printStackTrace(); 132 | } 133 | } 134 | } 135 | -------------------------------------------------------------------------------- /src/main/java/org/jsl/collider/SessionEmitterImpl.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2013 Sergey Zubarev, info@js-labs.org 3 | * 4 | * This file is a part of JS-Collider framework. 5 | * 6 | * This program is free software: you can redistribute it and/or modify 7 | * it under the terms of the GNU Affero General Public License as 8 | * published by the Free Software Foundation, either version 3 of the 9 | * License, or (at your option) any later version. 10 | * 11 | * This program is distributed in the hope that it will be useful, 12 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 13 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 | * GNU Affero General Public License for more details. 15 | * 16 | * You should have received a copy of the GNU Affero General Public License 17 | * along with this program. If not, see . 18 | */ 19 | 20 | package org.jsl.collider; 21 | 22 | import java.net.Socket; 23 | import java.net.SocketException; 24 | import java.nio.channels.SelectionKey; 25 | import java.nio.channels.SocketChannel; 26 | 27 | abstract class SessionEmitterImpl 28 | { 29 | protected final ColliderImpl m_collider; 30 | protected final RetainableDataBlockCache m_inputQueueDataBlockCache; 31 | private final SessionEmitter m_sessionEmitter; 32 | private final int m_joinMessageMaxSize; 33 | private final RetainableByteBufferPool m_joinPool; 34 | private final int m_forwardReadMaxSize; 35 | 36 | protected SessionEmitterImpl( 37 | ColliderImpl collider, 38 | RetainableDataBlockCache inputQueueDataBlockCache, 39 | SessionEmitter sessionEmitter, 40 | int joinMessageMaxSize, 41 | RetainableByteBufferPool joinPool ) 42 | { 43 | m_collider = collider; 44 | m_inputQueueDataBlockCache = inputQueueDataBlockCache; 45 | m_sessionEmitter = sessionEmitter; 46 | m_joinMessageMaxSize = joinMessageMaxSize; 47 | m_joinPool = joinPool; 48 | 49 | m_forwardReadMaxSize = 50 | ((sessionEmitter.forwardReadMaxSize == 0) 51 | ? collider.getConfig().forwardReadMaxSize 52 | : sessionEmitter.forwardReadMaxSize); 53 | } 54 | 55 | protected final void startSession( SocketChannel socketChannel, SelectionKey selectionKey ) 56 | { 57 | final int socketSendBufferSize = configureSocketChannel( socketChannel ); 58 | 59 | final SessionImpl sessionImpl = new SessionImpl( 60 | m_collider, socketChannel, selectionKey, socketSendBufferSize, m_joinMessageMaxSize, m_joinPool ); 61 | 62 | final Thread currentThread = Thread.currentThread(); 63 | addThread( currentThread ); 64 | final Session.Listener sessionListener = m_sessionEmitter.createSessionListener( sessionImpl ); 65 | removeThreadAndReleaseMonitor( currentThread ); 66 | 67 | /* Case when a sessionListener is null 68 | * will be handled inside the SessionImpl.initialize() 69 | */ 70 | sessionImpl.initialize( 71 | m_forwardReadMaxSize, m_inputQueueDataBlockCache, sessionListener ); 72 | } 73 | 74 | private int configureSocketChannel( SocketChannel socketChannel ) 75 | { 76 | final Socket socket = socketChannel.socket(); 77 | try 78 | { 79 | socket.setTcpNoDelay( m_sessionEmitter.tcpNoDelay ); 80 | } 81 | catch (final SocketException ex) 82 | { 83 | logException( ex ); 84 | } 85 | 86 | try 87 | { 88 | socket.setReuseAddress( m_sessionEmitter.reuseAddr ); 89 | } 90 | catch (final SocketException ex) 91 | { 92 | logException( ex ); 93 | } 94 | 95 | int recvBufferSize = m_sessionEmitter.socketRecvBufSize; 96 | if (recvBufferSize == 0) 97 | recvBufferSize = m_collider.getConfig().socketRecvBufSize; 98 | 99 | if (recvBufferSize > 0) 100 | { 101 | try 102 | { 103 | socket.setReceiveBufferSize( recvBufferSize ); 104 | } 105 | catch (final SocketException ex) 106 | { 107 | logException( ex ); 108 | } 109 | } 110 | 111 | int sendBufferSize = m_sessionEmitter.socketSendBufSize; 112 | if (sendBufferSize == 0) 113 | sendBufferSize = m_collider.getConfig().socketSendBufSize; 114 | 115 | if (sendBufferSize > 0) 116 | { 117 | try 118 | { 119 | socket.setSendBufferSize( sendBufferSize ); 120 | } 121 | catch (final SocketException ex) 122 | { 123 | logException( ex ); 124 | } 125 | } 126 | else 127 | sendBufferSize = (64 * 1024); 128 | 129 | return sendBufferSize; 130 | } 131 | 132 | protected abstract void addThread( Thread thread ); 133 | protected abstract void removeThreadAndReleaseMonitor( Thread thread ); 134 | protected abstract void logException( Exception ex ); 135 | 136 | public abstract void stopAndWait() throws InterruptedException; 137 | } 138 | -------------------------------------------------------------------------------- /tests/src/org/jsl/tests/pubsub/PubClient.java: -------------------------------------------------------------------------------- 1 | /* 2 | * JS-Collider framework tests. 3 | * Copyright (C) 2013 Sergey Zubarev 4 | * info@js-labs.org 5 | * 6 | * This program is free software: you can redistribute it and/or modify 7 | * it under the terms of the GNU Affero General Public License as 8 | * published by the Free Software Foundation, either version 3 of the 9 | * License, or (at your option) any later version. 10 | * 11 | * This program is distributed in the hope that it will be useful, 12 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 13 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 | * GNU Affero General Public License for more details. 15 | * 16 | * You should have received a copy of the GNU Affero General Public License 17 | * along with this program. If not, see . 18 | */ 19 | 20 | package org.jsl.tests.pubsub; 21 | 22 | import org.jsl.collider.*; 23 | 24 | import java.io.IOException; 25 | import java.net.InetSocketAddress; 26 | import java.nio.ByteBuffer; 27 | 28 | public class PubClient extends Thread 29 | { 30 | private final InetSocketAddress m_addr; 31 | private final int m_messages; 32 | private final int m_messageLength; 33 | private final int m_socketBufferSize; 34 | private long m_startTime; 35 | 36 | private class SessionListener implements Session.Listener, Runnable 37 | { 38 | private final Session m_session; 39 | private Thread m_thread; 40 | 41 | public SessionListener( Session session ) 42 | { 43 | m_session = session; 44 | 45 | System.out.println( 46 | "PubClient: connected " + session.getLocalAddress() + 47 | " -> " + session.getRemoteAddress() ); 48 | 49 | final ByteBuffer buf = ByteBuffer.allocateDirect( 5 ); 50 | buf.putInt(5); 51 | buf.put( (byte) 0 ); 52 | buf.position(0); 53 | session.sendData( buf ); 54 | } 55 | 56 | public void onDataReceived( RetainableByteBuffer data ) 57 | { 58 | /* All subscribers are connected to the server, 59 | * let's start. 60 | */ 61 | if (data.remaining() == 0) 62 | throw new AssertionError(); 63 | 64 | m_thread = new Thread( this ); 65 | m_thread.start(); 66 | } 67 | 68 | public void onConnectionClosed() 69 | { 70 | System.out.println( "PubClient: connection closed." ); 71 | if (m_thread != null) 72 | { 73 | try 74 | { 75 | m_thread.join(); 76 | } 77 | catch (final InterruptedException ex) 78 | { 79 | ex.printStackTrace(); 80 | } 81 | m_thread = null; 82 | } 83 | m_session.getCollider().stop(); 84 | } 85 | 86 | public void run() 87 | { 88 | final ByteBuffer buf = ByteBuffer.allocateDirect( m_messageLength ); 89 | buf.putInt( m_messageLength ); 90 | buf.putInt( m_messages ); 91 | for (int idx=8; idx. 18 | */ 19 | package org.jsl.tests.echo_latency; 20 | 21 | import org.jsl.collider.*; 22 | 23 | import java.io.IOException; 24 | import java.net.InetSocketAddress; 25 | import java.nio.ByteBuffer; 26 | import java.util.concurrent.atomic.AtomicInteger; 27 | 28 | public class Server 29 | { 30 | private final Client m_client; 31 | private final AtomicInteger m_sessionsDone; 32 | 33 | private class ServerListener implements Session.Listener 34 | { 35 | private final Session m_session; 36 | private final StreamDefragger m_streamDefragger; 37 | private int m_sessionsTotal; 38 | private int m_messages; 39 | 40 | private void onMessage( RetainableByteBuffer msg ) 41 | { 42 | final RetainableByteBuffer reply = msg.slice(); 43 | final int pos = msg.position(); 44 | final int messageLength = msg.getInt( pos ); 45 | 46 | if (msg.remaining() != messageLength) 47 | throw new RuntimeException( "invalid message" ); 48 | 49 | m_sessionsTotal = msg.getInt( pos+4 ); 50 | m_session.sendData( reply ); 51 | reply.release(); 52 | m_messages++; 53 | } 54 | 55 | public ServerListener( Session session ) 56 | { 57 | m_session = session; 58 | System.out.println( 59 | session.getLocalAddress() + " -> " + session.getRemoteAddress() + 60 | ": connection accepted" ); 61 | 62 | m_streamDefragger = new StreamDefragger(4) 63 | { 64 | protected int validateHeader( ByteBuffer header ) 65 | { 66 | return header.getInt( header.position() ); 67 | } 68 | }; 69 | } 70 | 71 | public void onDataReceived( RetainableByteBuffer data ) 72 | { 73 | final int remaining = data.remaining(); 74 | if (remaining == 0) 75 | throw new RuntimeException( "zero ByteBuffer" ); 76 | 77 | RetainableByteBuffer msg = m_streamDefragger.getNext( data ); 78 | while (msg != null) 79 | { 80 | if (msg == StreamDefragger.INVALID_HEADER) 81 | throw new RuntimeException( "Invalid packet received." ); 82 | 83 | onMessage( msg ); 84 | msg = m_streamDefragger.getNext(); 85 | } 86 | } 87 | 88 | public void onConnectionClosed() 89 | { 90 | m_streamDefragger.close(); 91 | 92 | System.out.println( 93 | m_session.getLocalAddress() + " -> " + m_session.getRemoteAddress() + 94 | ": connection closed (" + m_messages + ")." ); 95 | 96 | final int sessionsDone = m_sessionsDone.incrementAndGet(); 97 | if (sessionsDone == m_sessionsTotal) 98 | m_session.getCollider().stop(); 99 | } 100 | } 101 | 102 | private class TestAcceptor extends Acceptor 103 | { 104 | public TestAcceptor() 105 | { 106 | super(0); 107 | //inputQueueBlockSize = 700; 108 | } 109 | 110 | public void onAcceptorStarted( Collider collider, int localPort ) 111 | { 112 | System.out.println( "Echo latency server started at port " + localPort ); 113 | if (m_client != null) 114 | m_client.start( new InetSocketAddress("localhost", localPort) ); 115 | } 116 | 117 | public Session.Listener createSessionListener( Session session ) 118 | { 119 | return new ServerListener( session ); 120 | } 121 | } 122 | 123 | public Server( Client client ) 124 | { 125 | m_client = client; 126 | m_sessionsDone = new AtomicInteger(); 127 | } 128 | 129 | public void run() 130 | { 131 | try 132 | { 133 | final Collider collider = Collider.create(); 134 | collider.addAcceptor( new TestAcceptor() ); 135 | collider.run(); 136 | } 137 | catch (final IOException ex) 138 | { 139 | ex.printStackTrace(); 140 | } 141 | 142 | if (m_client != null) 143 | m_client.stopAndWait(); 144 | } 145 | 146 | public static void main( String [] args ) 147 | { 148 | new Server(null).run(); 149 | } 150 | } 151 | -------------------------------------------------------------------------------- /tests/src/org/jsl/tests/StreamDefragger.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2015 Sergey Zubarev, info@js-labs.org 3 | * 4 | * This file is a part of JS-Collider framework. 5 | * 6 | * This program is free software: you can redistribute it and/or modify 7 | * it under the terms of the GNU Affero General Public License as 8 | * published by the Free Software Foundation, either version 3 of the 9 | * License, or (at your option) any later version. 10 | * 11 | * This program is distributed in the hope that it will be useful, 12 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 13 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 | * GNU Affero General Public License for more details. 15 | * 16 | * You should have received a copy of the GNU Affero General Public License 17 | * along with this program. If not, see . 18 | */ 19 | 20 | package org.jsl.tests; 21 | 22 | import java.nio.ByteBuffer; 23 | 24 | public abstract class StreamDefragger 25 | { 26 | public final static ByteBuffer INVALID_HEADER = ByteBuffer.allocate(0); 27 | 28 | private final int m_headerSize; 29 | private ByteBuffer m_buf; 30 | private ByteBuffer m_data; 31 | private int m_packetLen; 32 | private int m_pos; 33 | private int m_limit; 34 | 35 | private static boolean copyData( ByteBuffer dst, ByteBuffer src, int bytes ) 36 | { 37 | final int pos = src.position(); 38 | final int limit = src.limit(); 39 | final int available = (limit - pos); 40 | if (available < bytes) 41 | { 42 | dst.put( src ); 43 | return false; 44 | } 45 | else 46 | { 47 | src.limit( pos + bytes ); 48 | dst.put( src ); 49 | src.limit( limit ); 50 | return true; 51 | } 52 | } 53 | 54 | private static ByteBuffer getBuffer( int capacity, boolean isDirect ) 55 | { 56 | if (capacity < 1024) 57 | capacity = 1024; 58 | if (isDirect) 59 | return ByteBuffer.allocateDirect( capacity ); 60 | else 61 | return ByteBuffer.allocate( capacity ); 62 | } 63 | 64 | public StreamDefragger( int headerSize ) 65 | { 66 | m_headerSize = headerSize; 67 | } 68 | 69 | public final ByteBuffer getNext( ByteBuffer data ) 70 | { 71 | assert( m_data == null ); 72 | 73 | if ((m_buf != null) && (m_buf.position() > 0)) 74 | { 75 | int pos = m_buf.position(); 76 | if (pos < m_headerSize) 77 | { 78 | int cc = (m_headerSize - pos); 79 | if (!copyData(m_buf, data, cc)) 80 | return null; 81 | 82 | m_buf.flip(); 83 | m_packetLen = validateHeader( m_buf ); 84 | if (m_packetLen <= 0) 85 | return INVALID_HEADER; 86 | 87 | if (m_buf.capacity() < m_packetLen) 88 | { 89 | ByteBuffer buf = ByteBuffer.allocate( m_packetLen ); 90 | m_buf.position( 0 ); 91 | m_buf.limit( m_headerSize ); 92 | buf.put( m_buf ); 93 | m_buf = buf; 94 | } 95 | else 96 | m_buf.limit( m_buf.capacity() ); 97 | 98 | pos = m_headerSize; 99 | m_buf.position( pos ); 100 | } 101 | 102 | int cc = (m_packetLen - pos); 103 | if (!copyData(m_buf, data, cc)) 104 | return null; 105 | m_buf.flip(); 106 | 107 | m_data = data; 108 | m_pos = data.position(); 109 | m_limit = data.limit(); 110 | 111 | return m_buf; 112 | } 113 | 114 | m_data = data; 115 | m_pos = data.position(); 116 | m_limit = data.limit(); 117 | return getNext(); 118 | } 119 | 120 | public final ByteBuffer getNext() 121 | { 122 | m_data.position( m_pos ); 123 | m_data.limit( m_limit ); 124 | 125 | final int bytesRemaining = (m_limit - m_pos); 126 | if (bytesRemaining == 0) 127 | { 128 | if (m_buf != null) 129 | m_buf.clear(); 130 | m_data = null; 131 | return null; 132 | } 133 | 134 | if (bytesRemaining < m_headerSize) 135 | { 136 | if (m_buf == null) 137 | m_buf = getBuffer( m_headerSize, m_data.isDirect() ); 138 | else 139 | m_buf.clear(); 140 | m_buf.put( m_data ); 141 | m_data = null; 142 | return null; 143 | } 144 | 145 | m_packetLen = validateHeader( m_data ); 146 | m_data.position( m_pos ); 147 | 148 | if (m_packetLen <= 0) 149 | return INVALID_HEADER; 150 | 151 | if (bytesRemaining < m_packetLen) 152 | { 153 | if ((m_buf == null) || (m_buf.capacity() < m_packetLen)) 154 | m_buf = getBuffer( m_packetLen, m_data.isDirect() ); 155 | else 156 | m_buf.clear(); 157 | m_buf.put( m_data ); 158 | m_data = null; 159 | return null; 160 | } 161 | 162 | m_pos += m_packetLen; 163 | m_data.limit( m_pos ); 164 | return m_data; 165 | } 166 | 167 | abstract protected int validateHeader( ByteBuffer header ); 168 | } 169 | -------------------------------------------------------------------------------- /tests/src/org/jsl/tests/echo_latency/Client.java: -------------------------------------------------------------------------------- 1 | /* 2 | * JS-Collider framework tests. 3 | * Copyright (C) 2015 Sergey Zubarev 4 | * info@js-labs.org 5 | * 6 | * This program is free software: you can redistribute it and/or modify 7 | * it under the terms of the GNU Affero General Public License as 8 | * published by the Free Software Foundation, either version 3 of the 9 | * License, or (at your option) any later version. 10 | * 11 | * This program is distributed in the hope that it will be useful, 12 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 13 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 | * GNU Affero General Public License for more details. 15 | * 16 | * You should have received a copy of the GNU Affero General Public License 17 | * along with this program. If not, see . 18 | */ 19 | 20 | package org.jsl.tests.echo_latency; 21 | 22 | import org.jsl.tests.Util; 23 | import java.io.IOException; 24 | import java.net.Socket; 25 | import java.net.InetAddress; 26 | import java.net.InetSocketAddress; 27 | import java.net.UnknownHostException; 28 | import java.nio.ByteBuffer; 29 | import java.nio.channels.SocketChannel; 30 | 31 | public class Client 32 | { 33 | private InetSocketAddress m_addr; 34 | private final int m_messages; 35 | private final ByteBuffer m_msg; 36 | private final Thread [] m_threads; 37 | 38 | private class SessionThread extends Thread 39 | { 40 | public void run() 41 | { 42 | try 43 | { 44 | final ByteBuffer msg = m_msg.duplicate(); 45 | final ByteBuffer buf = ByteBuffer.allocateDirect( m_msg.capacity() ); 46 | final SocketChannel socketChannel = SocketChannel.open( m_addr ); 47 | final Socket socket = socketChannel.socket(); 48 | socket.setTcpNoDelay( true ); 49 | System.out.println( 50 | "Client socket connected " + socket.getLocalSocketAddress() + 51 | " -> " + socket.getRemoteSocketAddress() + "." ); 52 | 53 | /* warming up */ 54 | for (int idx=0; idx<100; idx++) 55 | { 56 | socketChannel.write( msg ); 57 | msg.flip(); 58 | final int bytesReceived = socketChannel.read( buf ); 59 | assert( bytesReceived == m_msg.capacity() ); 60 | buf.clear(); 61 | } 62 | 63 | final long startTime = System.nanoTime(); 64 | for (int c=m_messages; c>0; c--) 65 | { 66 | socketChannel.write( msg ); 67 | msg.flip(); 68 | final int bytesReceived = socketChannel.read( buf ); 69 | assert( bytesReceived == m_msg.capacity() ); 70 | buf.clear(); 71 | } 72 | final long endTime = System.nanoTime(); 73 | socketChannel.close(); 74 | 75 | final int messages = (m_messages * 2); 76 | final long tm = ((endTime - startTime) / 1000); 77 | System.out.println( 78 | messages + " messages exchanged at " + 79 | Util.formatDelay(startTime, endTime) + " sec (" + 80 | (tm / messages) + " usec/msg)" ); 81 | } 82 | catch (final IOException ex) 83 | { 84 | ex.printStackTrace(); 85 | } 86 | } 87 | } 88 | 89 | public Client( int sessions, int messages, int messageLength ) 90 | { 91 | m_messages = messages; 92 | m_msg = ByteBuffer.allocateDirect( messageLength ); 93 | m_msg.putInt( messageLength ); 94 | m_msg.putInt( sessions ); 95 | for (int i=8; i " ); 131 | return; 132 | } 133 | 134 | try 135 | { 136 | final InetAddress addr = InetAddress.getByName( args[0] ); 137 | int portNumber = Integer.parseInt( args[1] ); 138 | int sessions = Integer.parseInt( args[2] ); 139 | int messages = Integer.parseInt( args[3] ); 140 | int messageLength = Integer.parseInt( args[4] ); 141 | 142 | new Client(sessions, messages, messageLength).start( new InetSocketAddress(addr, portNumber) ); 143 | } 144 | catch (final UnknownHostException ex) 145 | { 146 | ex.printStackTrace(); 147 | } 148 | } 149 | } 150 | -------------------------------------------------------------------------------- /tests/src/org/jsl/tests/message_queue/Main.java: -------------------------------------------------------------------------------- 1 | /* 2 | * JS-Collider framework tests. 3 | * Copyright (C) 2013 Sergey Zubarev 4 | * info@js-labs.org 5 | * 6 | * This program is free software: you can redistribute it and/or modify 7 | * it under the terms of the GNU Affero General Public License as 8 | * published by the Free Software Foundation, either version 3 of the 9 | * License, or (at your option) any later version. 10 | * 11 | * This program is distributed in the hope that it will be useful, 12 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 13 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 | * GNU Affero General Public License for more details. 15 | * 16 | * You should have received a copy of the GNU Affero General Public License 17 | * along with this program. If not, see . 18 | */ 19 | 20 | package org.jsl.tests.message_queue; 21 | 22 | import org.jsl.collider.MessageQueue; 23 | import org.jsl.collider.DataBlockCache; 24 | import org.jsl.collider.Util; 25 | 26 | import java.nio.ByteBuffer; 27 | import java.util.concurrent.atomic.AtomicInteger; 28 | 29 | public class Main 30 | { 31 | private static final int OPS = 1000000; 32 | private final AtomicInteger m_msgs; 33 | 34 | private class Worker extends Thread 35 | { 36 | private final int m_id; 37 | private final MessageQueue m_queue; 38 | private final int m_messageSize; 39 | 40 | public Worker( int id, MessageQueue queue, int messageSize ) 41 | { 42 | m_id = id; 43 | m_queue = queue; 44 | m_messageSize = messageSize; 45 | } 46 | 47 | public void run() 48 | { 49 | System.out.println( m_id + ": started" ); 50 | 51 | final ByteBuffer byteBuffer = ByteBuffer.allocateDirect( (1 + m_messageSize) * (Integer.SIZE/Byte.SIZE) ); 52 | byteBuffer.putInt( m_messageSize ); 53 | 54 | for (int idx=0, value=0x11111111; idx 0) 99 | throw new RuntimeException( "Unexpected message size, should be empty." ); 100 | ret++; 101 | 102 | msg = queue.getNext(); 103 | if (msg == null) 104 | break; 105 | } 106 | } 107 | catch (Exception ex) 108 | { 109 | final int pos = msg.position(); 110 | msg.limit( msg.capacity() ).position( 0 ); 111 | System.out.println( ex.toString() ); 112 | System.out.println( "pos=0x" + String.format("%x", pos) + "\n" + Util.hexDump(msg) ); 113 | } 114 | return ret; 115 | } 116 | 117 | private Main() 118 | { 119 | m_msgs = new AtomicInteger(0); 120 | } 121 | 122 | private void run() 123 | { 124 | final DataBlockCache dataBlockCache = new DataBlockCache( true, 4*1024, 4, 64 ); 125 | final MessageQueue queue = new MessageQueue( dataBlockCache ); 126 | final Thread [] thread = new Thread[3]; 127 | 128 | for (int idx=0; idx. 18 | */ 19 | 20 | package org.jsl.tests.recv_throughput; 21 | 22 | import org.jsl.collider.*; 23 | import org.jsl.tests.Util; 24 | 25 | import java.io.IOException; 26 | import java.net.InetSocketAddress; 27 | import java.nio.ByteBuffer; 28 | import java.util.concurrent.atomic.AtomicInteger; 29 | 30 | public class Server 31 | { 32 | private final Client m_client; 33 | private final int m_socketRecvBufSize; 34 | private final AtomicInteger m_sessionsDone; 35 | 36 | private class ServerListener implements Session.Listener 37 | { 38 | private final Session m_session; 39 | private final StreamDefragger m_stream; 40 | private int m_callbacks; 41 | private int m_messagesTotal; 42 | private int m_messagesReceived; 43 | private int m_bytesTotal; 44 | private int m_sessionsTotal; 45 | private long m_startTime; 46 | 47 | public ServerListener( Session session ) 48 | { 49 | m_session = session; 50 | m_stream = new StreamDefragger(4) 51 | { 52 | protected int validateHeader( ByteBuffer header ) 53 | { 54 | return header.getInt( header.position() ); 55 | } 56 | }; 57 | System.out.println( 58 | session.getLocalAddress() + " -> " + session.getRemoteAddress() + 59 | ": connection accepted" ); 60 | } 61 | 62 | public void onDataReceived( RetainableByteBuffer data ) 63 | { 64 | if (data.remaining() == 0) 65 | throw new AssertionError(); 66 | 67 | m_bytesTotal += data.remaining(); 68 | m_callbacks++; 69 | RetainableByteBuffer msg = m_stream.getNext( data ); 70 | while (msg != null) 71 | { 72 | if (m_messagesTotal == 0) 73 | { 74 | m_startTime = System.nanoTime(); 75 | msg.getInt(); // skip length 76 | m_messagesTotal = msg.getInt(); 77 | m_sessionsTotal = msg.getInt(); 78 | } 79 | m_messagesReceived++; 80 | msg = m_stream.getNext(); 81 | } 82 | 83 | if (m_messagesReceived == m_messagesTotal) 84 | { 85 | final long endTime = System.nanoTime(); 86 | double tm = (endTime - m_startTime) / 1000; 87 | tm /= 1000000.0; 88 | tm = (m_messagesReceived / tm); 89 | System.out.println( m_session.getRemoteAddress() + ": received " + 90 | m_messagesReceived + " messages (" + 91 | m_bytesTotal + " bytes) at " + Util.formatDelay(m_startTime, endTime) + 92 | " sec (" + (int)tm + " msgs/sec), " + m_callbacks + " callbacks." ); 93 | } 94 | } 95 | 96 | public void onConnectionClosed() 97 | { 98 | System.out.println( 99 | m_session.getLocalAddress() + " -> " + m_session.getRemoteAddress() + 100 | ": connection closed" ); 101 | final int sessionsDone = m_sessionsDone.incrementAndGet(); 102 | if (sessionsDone == m_sessionsTotal) 103 | m_session.getCollider().stop(); 104 | } 105 | } 106 | 107 | private class TestAcceptor extends Acceptor 108 | { 109 | public TestAcceptor() 110 | { 111 | socketRecvBufSize = m_socketRecvBufSize; 112 | } 113 | 114 | public void onAcceptorStarted( Collider collider, int portNumber ) 115 | { 116 | System.out.println( "Server started at port " + portNumber ); 117 | if (m_client != null) 118 | m_client.start( new InetSocketAddress("localhost", portNumber) ); 119 | } 120 | 121 | public Session.Listener createSessionListener( Session session ) 122 | { 123 | return new ServerListener( session ); 124 | } 125 | } 126 | 127 | public Server( Client client, int socketRecvBufSize ) 128 | { 129 | m_client = client; 130 | m_socketRecvBufSize = socketRecvBufSize; 131 | m_sessionsDone = new AtomicInteger(0); 132 | } 133 | 134 | public void run() 135 | { 136 | try 137 | { 138 | final Collider collider = Collider.create(); 139 | collider.addAcceptor( new TestAcceptor() ); 140 | collider.run(); 141 | m_client.stopAndWait(); 142 | } 143 | catch (final IOException ex) 144 | { 145 | ex.printStackTrace(); 146 | } 147 | } 148 | 149 | public static void main( String [] args ) 150 | { 151 | } 152 | } 153 | -------------------------------------------------------------------------------- /tests/src/org/jsl/tests/binary_queue/Main.java: -------------------------------------------------------------------------------- 1 | /* 2 | * JS-Collider framework. 3 | * Copyright (C) 2015 Sergey Zubarev 4 | * info@js-labs.org 5 | * 6 | * This program is free software: you can redistribute it and/or modify 7 | * it under the terms of the GNU General Public License as published by 8 | * the Free Software Foundation, either version 3 of the License, or 9 | * (at your option) any later version. 10 | * This program is distributed in the hope that it will be useful, 11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. 13 | * See the GNU General Public License for more details. 14 | * You should have received a copy of the GNU General Public License 15 | * along with this program. If not, see . 16 | */ 17 | 18 | package org.jsl.tests.binary_queue; 19 | 20 | import org.jsl.collider.DataBlockCache; 21 | import org.jsl.tests.StreamDefragger; 22 | import org.jsl.tests.BinaryQueue; 23 | import org.jsl.tests.Util; 24 | 25 | import java.nio.ByteBuffer; 26 | import java.util.concurrent.Semaphore; 27 | import java.util.concurrent.atomic.AtomicLong; 28 | import java.util.logging.Level; 29 | import java.util.logging.Logger; 30 | 31 | 32 | public class Main 33 | { 34 | private static final int MESSAGE_MAGIC = 0x1A2B3C4D; 35 | private static final Logger s_logger = Logger.getLogger( Main.class.getName() ); 36 | 37 | private final Semaphore m_sema; 38 | private final AtomicLong m_state; 39 | private final DataBlockCache m_dataBlockCache; 40 | private final BinaryQueue m_outputQueue; 41 | private final Stream m_stream; 42 | private int m_waitMessages; 43 | private int m_messages; 44 | 45 | private class Stream extends StreamDefragger 46 | { 47 | public Stream() 48 | { 49 | super( 4 ); 50 | } 51 | 52 | protected int validateHeader( ByteBuffer header ) 53 | { 54 | return header.getInt(); 55 | } 56 | } 57 | 58 | private Main() 59 | { 60 | m_sema = new Semaphore(0); 61 | m_state = new AtomicLong(0); 62 | m_dataBlockCache = new DataBlockCache( false, 1000, 20, 100 ); 63 | m_outputQueue = new BinaryQueue( m_dataBlockCache ); 64 | m_stream = new Stream(); 65 | m_waitMessages = 0; 66 | m_messages = 0; 67 | } 68 | 69 | private void startGenerator( int messages, int messageSize ) 70 | { 71 | new Generator(this, messages, messageSize, MESSAGE_MAGIC).start(); 72 | m_waitMessages += messages; 73 | } 74 | 75 | private void run() 76 | { 77 | final int MESSAGES = 10000; 78 | 79 | this.startGenerator( MESSAGES, 5000 ); 80 | this.startGenerator( MESSAGES, 3280 ); 81 | this.startGenerator( MESSAGES, 126 ); 82 | this.startGenerator( MESSAGES, 1000 ); 83 | this.startGenerator( MESSAGES, 300 ); 84 | this.startGenerator( MESSAGES, 510 ); 85 | this.startGenerator( MESSAGES, 4576 ); 86 | this.startGenerator( MESSAGES, 777 ); 87 | this.startGenerator( MESSAGES, 4 ); 88 | 89 | ByteBuffer [] iov = new ByteBuffer[4]; 90 | long bytesProcessed = 0; 91 | int waits = 0; 92 | 93 | /* 94 | try { Thread.sleep(4000); } 95 | catch (InterruptedException ignored) {} 96 | */ 97 | 98 | long startTime = System.nanoTime(); 99 | while (m_messages < m_waitMessages) 100 | { 101 | try { m_sema.acquire(); } 102 | catch (InterruptedException ex) 103 | { System.out.println(ex.toString()); } 104 | 105 | long state = m_state.get(); 106 | assert( state > 0 ); 107 | while (state > 0) 108 | { 109 | long bytesReady = m_outputQueue.getData( iov, state ); 110 | int pos0 = iov[0].position(); 111 | for (int idx=0; idx 4) 118 | { 119 | final int magic = msg.getInt(); 120 | assert( magic == MESSAGE_MAGIC ); 121 | } 122 | m_messages++; 123 | msg = m_stream.getNext(); 124 | } 125 | } 126 | for (int idx=0; idx 0) 147 | { 148 | final long state = m_state.addAndGet( bytesReady ); 149 | if (state == bytesReady) 150 | m_sema.release(); 151 | } 152 | } 153 | 154 | public void putInt( int value ) 155 | { 156 | final int bytesReady = m_outputQueue.putInt( value ); 157 | if (bytesReady > 0) 158 | { 159 | final long state = m_state.addAndGet( bytesReady ); 160 | if (state == bytesReady) 161 | m_sema.release(); 162 | } 163 | } 164 | 165 | public static void main(String[] args) 166 | { 167 | new Main().run(); 168 | } 169 | } 170 | -------------------------------------------------------------------------------- /src/main/java/org/jsl/collider/Collider.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2013 Sergey Zubarev, info@js-labs.org 3 | * 4 | * This file is a part of JS-Collider framework. 5 | * 6 | * This program is free software: you can redistribute it and/or modify 7 | * it under the terms of the GNU Affero General Public License as 8 | * published by the Free Software Foundation, either version 3 of the 9 | * License, or (at your option) any later version. 10 | * 11 | * This program is distributed in the hope that it will be useful, 12 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 13 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 | * GNU Affero General Public License for more details. 15 | * 16 | * You should have received a copy of the GNU Affero General Public License 17 | * along with this program. If not, see . 18 | */ 19 | 20 | package org.jsl.collider; 21 | 22 | import java.io.IOException; 23 | import java.net.NetworkInterface; 24 | import java.nio.ByteOrder; 25 | 26 | /* Collider public API. Typical usage example: 27 | *
{@code
 28 |  * class ColliderApplication
 29 |  * {
 30 |  *     public static void main( String [] args )
 31 |  *     {
 32 |  *         try {
 33 |  *             final Collider collider = Collider.create();
 34 |  *             collider.run();
 35 |  *         }
 36 |  *         catch (IOException ex) {
 37 |  *             ex.printStackTrace();
 38 |  *         }
 39 |  *     }
 40 |  * }
41 | */ 42 | 43 | public abstract class Collider 44 | { 45 | public static class Config 46 | { 47 | public int threadPriority; 48 | public int threadPoolThreads; 49 | public boolean useDirectBuffers; 50 | public ByteOrder byteOrder; 51 | 52 | public int socketSendBufSize; 53 | public int socketRecvBufSize; 54 | public int forwardReadMaxSize; 55 | public int inputQueueBlockSize; 56 | public int inputQueueCacheMaxSize; 57 | public int joinMessageMaxSize; 58 | public int datagramReadMinSize; 59 | 60 | public Config() 61 | { 62 | threadPriority = Thread.NORM_PRIORITY; 63 | threadPoolThreads = 0; /* by default = number of cores */ 64 | useDirectBuffers = true; 65 | byteOrder = ByteOrder.nativeOrder(); 66 | 67 | socketSendBufSize = 0; /* Use system default settings by default */ 68 | socketRecvBufSize = 0; /* Use system default settings by default */ 69 | 70 | forwardReadMaxSize = (256 * 1024); 71 | inputQueueBlockSize = (64 * 1024); 72 | inputQueueCacheMaxSize = 128; 73 | joinMessageMaxSize = 0; 74 | datagramReadMinSize = (2 * 1024); 75 | } 76 | } 77 | 78 | private final Config m_config; 79 | 80 | protected Collider( Config config ) 81 | { 82 | m_config = config; 83 | } 84 | 85 | public final Config getConfig() 86 | { 87 | return m_config; 88 | } 89 | 90 | /** 91 | * Starts the collider. 92 | * Do not return the control back while collider is not stopped. 93 | */ 94 | public abstract void run(); 95 | 96 | /** 97 | * Stops the running collider. 98 | * The call is asynchronous, collider can still run some time. 99 | * At first all currently running acceptors and connectors will be removed. 100 | * After that all established sessions will be closed 101 | * (Session.Listener.onConnectionClose() will be called for each). 102 | * Only after that collider run loop stops and the thread running 103 | * Collider.run() function gets control back. 104 | */ 105 | public abstract void stop(); 106 | 107 | /** 108 | * Adds an Acceptor to the collider. User supposed to extend {@link Acceptor} 109 | * class with handlers to be called by the collider when it will be ready to accept 110 | * inbound socket connections and when new inbound socket connection appears. 111 | * Additionally to the server socket exceptions 112 | * throws an IOException in a case if collider already stopped. 113 | * @param acceptor The acceptor instance to add 114 | * @throws IOException if collider failed to create accepting socket or collider already stopped 115 | */ 116 | public abstract void addAcceptor(Acceptor acceptor) throws IOException; 117 | 118 | /** 119 | * Removes the Acceptor from the collider. 120 | * On return guarantees no one thread runs 121 | * Acceptor.onAcceptorStarted or Acceptor.createSessionListener. 122 | * @param acceptor The acceptor instance to remove 123 | * @throws InterruptedException if thread was interrupted on wait 124 | */ 125 | public abstract void removeAcceptor(Acceptor acceptor) throws InterruptedException; 126 | 127 | /** 128 | * Adds Connector to the collider. 129 | * Operation is asynchronous, socket operations can throw an exception, 130 | * Connector.onException will be called in this case in the collider thread pool, 131 | * better to handle it properly. 132 | * @param connector The connector instance to add 133 | */ 134 | public abstract void addConnector(Connector connector); 135 | public abstract void removeConnector(Connector connector) throws InterruptedException; 136 | 137 | public abstract void addDatagramListener( 138 | DatagramListener datagramListener ) throws IOException; 139 | 140 | public abstract void addDatagramListener( 141 | DatagramListener datagramListener, 142 | NetworkInterface networkInterface ) throws IOException; 143 | 144 | public abstract void removeDatagramListener( 145 | DatagramListener datagramListener ) throws InterruptedException; 146 | 147 | public abstract ThreadPool getThreadPool(); 148 | 149 | /** 150 | * Create a Collider instance with default configuration. 151 | * @return a new collider instance 152 | * @throws IOException if selector I/O error occurs 153 | */ 154 | public static Collider create() throws IOException 155 | { 156 | return create(new Config()); 157 | } 158 | 159 | public static Collider create(Config config) throws IOException 160 | { 161 | return new ColliderImpl(config); 162 | } 163 | } 164 | -------------------------------------------------------------------------------- /tests/src/org/jsl/tests/session_throughput/Server.java: -------------------------------------------------------------------------------- 1 | /* 2 | * JS-Collider framework tests. 3 | * Copyright (C) 2022 Sergey Zubarev 4 | * info@js-labs.org 5 | * 6 | * This program is free software: you can redistribute it and/or modify 7 | * it under the terms of the GNU Affero General Public License as 8 | * published by the Free Software Foundation, either version 3 of the 9 | * License, or (at your option) any later version. 10 | * 11 | * This program is distributed in the hope that it will be useful, 12 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 13 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 | * GNU Affero General Public License for more details. 15 | * 16 | * You should have received a copy of the GNU Affero General Public License 17 | * along with this program. If not, see . 18 | */ 19 | 20 | package org.jsl.tests.session_throughput; 21 | 22 | import org.jsl.collider.*; 23 | import java.io.IOException; 24 | import java.net.InetSocketAddress; 25 | import java.nio.ByteBuffer; 26 | import java.util.HashSet; 27 | import java.util.concurrent.atomic.AtomicInteger; 28 | import java.util.concurrent.locks.ReentrantLock; 29 | 30 | public class Server 31 | { 32 | private final Client m_client; 33 | private final int m_socketBufferSize; 34 | 35 | private final AtomicInteger m_sessions; 36 | private final ReentrantLock m_lock; 37 | private final HashSet m_clients; 38 | 39 | private class ServerListener implements Session.Listener 40 | { 41 | private final Session m_session; 42 | private final StreamDefragger m_streamDefragger; 43 | private int m_messagesReceived; 44 | 45 | public ServerListener( Session session ) 46 | { 47 | m_session = session; 48 | m_streamDefragger = new StreamDefragger(4) 49 | { 50 | protected int validateHeader( ByteBuffer header ) 51 | { 52 | final int pos = header.position(); 53 | return header.getInt( pos ); 54 | } 55 | }; 56 | 57 | System.out.println( session.getRemoteAddress() + ": connection accepted" ); 58 | } 59 | 60 | public void onDataReceived( RetainableByteBuffer data ) 61 | { 62 | assert( data.remaining() > 0 ); 63 | 64 | RetainableByteBuffer msg = m_streamDefragger.getNext( data ); 65 | while (msg != null) 66 | { 67 | final int position = msg.position(); 68 | final int remaining = msg.remaining(); 69 | final int messageLength = msg.getInt(); 70 | if (remaining != messageLength) 71 | throw new AssertionError(); 72 | 73 | if (m_messagesReceived++ == 0) 74 | { 75 | final int sessions = msg.getInt(); 76 | 77 | m_lock.lock(); 78 | try 79 | { 80 | m_clients.add( m_session ); 81 | } 82 | finally 83 | { 84 | m_lock.unlock(); 85 | } 86 | 87 | if (m_sessions.incrementAndGet() == sessions) 88 | { 89 | System.out.println( "All clients connected, starting test." ); 90 | 91 | msg.position( position ); 92 | final RetainableByteBuffer reply = msg.slice(); 93 | 94 | for (Session session : m_clients) 95 | session.sendData( reply ); 96 | 97 | reply.release(); 98 | } 99 | } 100 | else 101 | { 102 | msg.position( position ); 103 | final RetainableByteBuffer reply = msg.slice(); 104 | 105 | /* m_clients will not be modified any more, can access it safely. */ 106 | for (Session session : m_clients) 107 | session.sendData( reply ); 108 | 109 | reply.release(); 110 | } 111 | msg = m_streamDefragger.getNext(); 112 | } 113 | } 114 | 115 | public void onConnectionClosed() 116 | { 117 | final int sessions = m_sessions.decrementAndGet(); 118 | if (sessions < 0) 119 | throw new AssertionError(); 120 | if (sessions == 0) 121 | m_session.getCollider().stop(); 122 | 123 | System.out.println( m_session.getRemoteAddress() + ": connection closed" ); 124 | } 125 | } 126 | 127 | private class TestAcceptor extends Acceptor 128 | { 129 | public TestAcceptor() 130 | { 131 | super(0); 132 | tcpNoDelay = true; 133 | socketRecvBufSize = m_socketBufferSize; 134 | socketSendBufSize = m_socketBufferSize; 135 | } 136 | 137 | public void onAcceptorStarted( Collider collider, int localPort ) 138 | { 139 | System.out.println( "Session throughput server started at port " + localPort ); 140 | if (m_client != null) 141 | m_client.start( new InetSocketAddress("localhost", localPort) ); 142 | } 143 | 144 | public Session.Listener createSessionListener( Session session ) 145 | { 146 | return new ServerListener( session ); 147 | } 148 | } 149 | 150 | public Server( Client client, int socketBufferSize ) 151 | { 152 | m_client = client; 153 | m_socketBufferSize = socketBufferSize; 154 | m_sessions = new AtomicInteger(); 155 | m_lock = new ReentrantLock(); 156 | m_clients = new HashSet(); 157 | } 158 | 159 | public void run() 160 | { 161 | try 162 | { 163 | final Collider collider = Collider.create(); 164 | collider.addAcceptor( new TestAcceptor() ); 165 | collider.run(); 166 | } 167 | catch (final IOException ex) 168 | { 169 | ex.printStackTrace(); 170 | } 171 | 172 | if (m_client != null) 173 | m_client.stopAndWait(); 174 | } 175 | } 176 | -------------------------------------------------------------------------------- /tests/src/org/jsl/tests/session_latency/Server.java: -------------------------------------------------------------------------------- 1 | /* 2 | * JS-Collider framework tests. 3 | * Copyright (C) 2022 Sergey Zubarev 4 | * info@js-labs.org 5 | * 6 | * This program is free software: you can redistribute it and/or modify 7 | * it under the terms of the GNU Affero General Public License as 8 | * published by the Free Software Foundation, either version 3 of the 9 | * License, or (at your option) any later version. 10 | * 11 | * This program is distributed in the hope that it will be useful, 12 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 13 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 | * GNU Affero General Public License for more details. 15 | * 16 | * You should have received a copy of the GNU Affero General Public License 17 | * along with this program. If not, see . 18 | */ 19 | 20 | package org.jsl.tests.session_latency; 21 | 22 | import org.jsl.collider.*; 23 | 24 | import java.io.IOException; 25 | import java.net.InetSocketAddress; 26 | import java.nio.ByteBuffer; 27 | import java.util.concurrent.atomic.AtomicInteger; 28 | import java.util.concurrent.atomic.AtomicReference; 29 | 30 | public class Server 31 | { 32 | private final int m_sessions; 33 | private final int m_messages; 34 | private final ByteBuffer m_msg; 35 | private final ByteBuffer m_msgStop; 36 | private final Client m_client; 37 | private final AtomicReference m_lastListener; 38 | private final AtomicInteger m_sessionsDone; 39 | 40 | private class ServerListener implements Session.Listener 41 | { 42 | private final Session m_session; 43 | private final StreamDefragger m_defragger; 44 | private Session m_session2; 45 | private int m_messages; 46 | 47 | public ServerListener( Session session ) 48 | { 49 | m_session = session; 50 | m_defragger = new StreamDefragger(Integer.SIZE / Byte.SIZE) { 51 | @Override 52 | protected int validateHeader(ByteBuffer header) { 53 | final int pos = header.position(); 54 | return header.getInt(pos); 55 | } 56 | }; 57 | 58 | for (;;) 59 | { 60 | final ServerListener lastListener = m_lastListener.get(); 61 | if (lastListener == null) 62 | { 63 | if (m_lastListener.compareAndSet(null, this)) 64 | break; 65 | } 66 | else 67 | { 68 | if (m_lastListener.compareAndSet(lastListener, null)) 69 | { 70 | System.out.println( 71 | session.getRemoteAddress() + " <-> " + lastListener.m_session.getRemoteAddress() ); 72 | lastListener.m_session2 = session; 73 | m_session2 = lastListener.m_session; 74 | m_session.sendData( m_msg ); 75 | break; 76 | } 77 | } 78 | } 79 | } 80 | 81 | public void onDataReceived(RetainableByteBuffer data) 82 | { 83 | RetainableByteBuffer msg = m_defragger.getNext(data); 84 | while (msg != null) 85 | { 86 | if (++m_messages < Server.this.m_messages) 87 | { 88 | final RetainableByteBuffer reply = msg.duplicate(); 89 | m_session2.sendData(reply); 90 | reply.release(); 91 | } 92 | else 93 | { 94 | m_session.sendData(m_msgStop); 95 | m_session.closeConnection(); 96 | m_session2.sendData(m_msgStop); 97 | m_session2.closeConnection(); 98 | } 99 | msg = m_defragger.getNext(); 100 | } 101 | } 102 | 103 | public void onConnectionClosed() 104 | { 105 | System.out.println( "Connection closed to " + m_session.getRemoteAddress() ); 106 | final int sessionsDone = m_sessionsDone.incrementAndGet(); 107 | if (sessionsDone == m_sessions) 108 | m_session.getCollider().stop(); 109 | m_defragger.close(); 110 | } 111 | } 112 | 113 | private class TestAcceptor extends Acceptor 114 | { 115 | public TestAcceptor() 116 | { 117 | super(0); 118 | tcpNoDelay = true; 119 | } 120 | 121 | public void onAcceptorStarted( Collider collider, int localPort ) 122 | { 123 | System.out.println( "Latency test server started at port " + localPort ); 124 | if (m_client != null) 125 | m_client.start( new InetSocketAddress("localhost", localPort) ); 126 | } 127 | 128 | public Session.Listener createSessionListener( Session session ) 129 | { 130 | return new ServerListener( session ); 131 | } 132 | } 133 | 134 | public Server( int sessions, int messages, int messageLength, Client client ) 135 | { 136 | m_sessions = sessions; 137 | m_messages = messages; 138 | m_msg = ByteBuffer.allocateDirect( messageLength ); 139 | m_msg.putInt( 0, messageLength ); 140 | for (int idx=4; idx(); 147 | m_sessionsDone = new AtomicInteger(); 148 | } 149 | 150 | public void run() 151 | { 152 | try 153 | { 154 | /* Would be better to avoid message fragmentation. */ 155 | final Collider.Config config = new Collider.Config(); 156 | if (m_msg.capacity() > config.inputQueueBlockSize) 157 | config.inputQueueBlockSize = m_msg.capacity(); 158 | 159 | final Collider collider = Collider.create( config ); 160 | collider.addAcceptor( new TestAcceptor() ); 161 | collider.run(); 162 | } 163 | catch (final IOException ex) 164 | { 165 | ex.printStackTrace(); 166 | } 167 | 168 | if (m_client != null) 169 | m_client.stopAndWait(); 170 | } 171 | } 172 | -------------------------------------------------------------------------------- /tests/src/org/jsl/tests/recv_throughput/Client.java: -------------------------------------------------------------------------------- 1 | /* 2 | * JS-Collider framework tests. 3 | * Copyright (C) 2013 Sergey Zubarev 4 | * info@js-labs.org 5 | * 6 | * This program is free software: you can redistribute it and/or modify 7 | * it under the terms of the GNU Affero General Public License as 8 | * published by the Free Software Foundation, either version 3 of the 9 | * License, or (at your option) any later version. 10 | * 11 | * This program is distributed in the hope that it will be useful, 12 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 13 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 | * GNU Affero General Public License for more details. 15 | * 16 | * You should have received a copy of the GNU Affero General Public License 17 | * along with this program. If not, see . 18 | */ 19 | 20 | package org.jsl.tests.recv_throughput; 21 | 22 | import org.jsl.tests.Util; 23 | import java.io.IOException; 24 | import java.net.InetAddress; 25 | import java.net.InetSocketAddress; 26 | import java.net.UnknownHostException; 27 | import java.net.Socket; 28 | import java.nio.ByteBuffer; 29 | import java.nio.channels.SocketChannel; 30 | 31 | public class Client 32 | { 33 | private InetSocketAddress m_addr; 34 | private final int m_messages; 35 | private final int m_messageLength; 36 | private final int m_socketSendBufferSize; 37 | private final ByteBuffer m_batch; 38 | private Thread [] m_threads; 39 | 40 | private class SessionThread extends Thread 41 | { 42 | public void run() 43 | { 44 | try 45 | { 46 | final SocketChannel socketChannel = SocketChannel.open( m_addr ); 47 | final Socket socket = socketChannel.socket(); 48 | socket.setTcpNoDelay( true ); 49 | socket.setSendBufferSize( m_socketSendBufferSize ); 50 | 51 | System.out.println( 52 | "Client connected " + socket.getLocalSocketAddress() + 53 | " -> " + socket.getRemoteSocketAddress() + "." ); 54 | 55 | final int batchMessages = (m_batch.capacity() / m_messageLength); 56 | final long startTime = System.nanoTime(); 57 | final ByteBuffer batch = m_batch.duplicate(); 58 | int messagesRemaining = m_messages; 59 | 60 | while (messagesRemaining > batchMessages) 61 | { 62 | socketChannel.write( batch ); 63 | batch.position(0); 64 | messagesRemaining -= batchMessages; 65 | } 66 | batch.limit( messagesRemaining * m_messageLength ); 67 | socketChannel.write( batch ); 68 | final long endTime = System.nanoTime(); 69 | socketChannel.close(); 70 | 71 | double tm = ((endTime - startTime) / 1000); 72 | tm /= 1000000; 73 | tm = (m_messages / tm); 74 | System.out.println( "Sent " + m_messages + " messages (" + 75 | m_messages*m_messageLength + " bytes) at " + 76 | Util.formatDelay(startTime, endTime) + " sec (" + 77 | (int)tm + " msgs/sec)." ); 78 | } 79 | catch (IOException ex) 80 | { 81 | ex.printStackTrace(); 82 | } 83 | } 84 | } 85 | 86 | public Client( int sessions, int messages, int messageLength, int socketSendBufferSize ) 87 | { 88 | if (messageLength < 12) 89 | messageLength = 12; 90 | 91 | m_messages = messages; 92 | m_messageLength = messageLength; 93 | m_socketSendBufferSize = socketSendBufferSize; 94 | 95 | int batchSize = (messages * messageLength); 96 | if (batchSize > 1024*1024) 97 | batchSize = 1024*1024; 98 | 99 | int batchMessages = (batchSize / messageLength); 100 | batchSize = (batchMessages * messageLength); 101 | 102 | m_batch = ByteBuffer.allocateDirect( batchSize ); 103 | for (int idx=0; idx [] [] []" ); 150 | return; 151 | } 152 | 153 | InetSocketAddress addr; 154 | try 155 | { 156 | final int portNumber = Integer.parseInt( args[1] ); 157 | addr = new InetSocketAddress( InetAddress.getByName(args[0]), portNumber ); 158 | } 159 | catch (UnknownHostException ex) 160 | { 161 | ex.printStackTrace(); 162 | return; 163 | } 164 | 165 | if (args.length <= 2) 166 | { 167 | sessions = Integer.parseInt( args[2] ); 168 | if (args.length <= 3) 169 | { 170 | messages = Integer.parseInt( args[3] ); 171 | if (args.length <= 4) 172 | messageLength = Integer.parseInt( args[4] ); 173 | } 174 | } 175 | 176 | new Client(sessions, messages, messageLength, socketSendBufferSize).start( addr ); 177 | } 178 | } 179 | -------------------------------------------------------------------------------- /tests/src/org/jsl/tests/echo_throughput/Server.java: -------------------------------------------------------------------------------- 1 | /* 2 | * JS-Collider framework tests. 3 | * Copyright (C) 2022 Sergey Zubarev 4 | * info@js-labs.org 5 | * 6 | * This program is free software: you can redistribute it and/or modify 7 | * it under the terms of the GNU Affero General Public License as 8 | * published by the Free Software Foundation, either version 3 of the 9 | * License, or (at your option) any later version. 10 | * 11 | * This program is distributed in the hope that it will be useful, 12 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 13 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 | * GNU Affero General Public License for more details. 15 | * 16 | * You should have received a copy of the GNU Affero General Public License 17 | * along with this program. If not, see . 18 | */ 19 | 20 | package org.jsl.tests.echo_throughput; 21 | 22 | import org.jsl.collider.Collider; 23 | import org.jsl.collider.Session; 24 | import org.jsl.collider.StreamDefragger; 25 | import org.jsl.collider.RetainableByteBuffer; 26 | import org.jsl.collider.Acceptor; 27 | import org.jsl.tests.Util; 28 | 29 | import java.io.IOException; 30 | import java.net.InetSocketAddress; 31 | import java.nio.ByteBuffer; 32 | import java.util.concurrent.atomic.AtomicInteger; 33 | 34 | public class Server 35 | { 36 | private final Client m_client; 37 | private final AtomicInteger m_sessionsDone; 38 | 39 | private class ServerListener implements Session.Listener 40 | { 41 | private final Session m_session; 42 | private final StreamDefragger m_stream; 43 | private int m_messagesExpected; 44 | private int m_sessionsExpected; 45 | private int m_messagesReceived; 46 | private int m_bytesReceived; 47 | private long m_startTime; 48 | 49 | public ServerListener( Session session ) 50 | { 51 | m_session = session; 52 | m_stream = new StreamDefragger(4) 53 | { 54 | protected int validateHeader( ByteBuffer header ) 55 | { 56 | final int pos = header.position(); 57 | return header.getInt( pos ); 58 | } 59 | }; 60 | System.out.println( 61 | session.getLocalAddress() + " -> " + session.getRemoteAddress() + 62 | ": connection accepted" ); 63 | } 64 | 65 | public void onDataReceived( RetainableByteBuffer data ) 66 | { 67 | final int bytesReceived = data.remaining(); 68 | if (bytesReceived == 0) 69 | throw new AssertionError(); 70 | m_bytesReceived += bytesReceived; 71 | 72 | RetainableByteBuffer msg = m_stream.getNext( data ); 73 | while (msg != null) 74 | { 75 | final int pos = msg.position(); 76 | final int bytesReady = msg.remaining(); 77 | final int messageLength = msg.getInt( pos ); 78 | if (messageLength != bytesReady) 79 | throw new AssertionError(); 80 | 81 | final int sessionsExpected = msg.getInt( pos + (Integer.SIZE/Byte.SIZE) ); 82 | final int messagesExpected = msg.getInt( pos + 2*(Integer.SIZE/Byte.SIZE) ); 83 | if (m_messagesExpected == 0) 84 | { 85 | m_startTime = System.nanoTime(); 86 | m_sessionsExpected = sessionsExpected; 87 | m_messagesExpected = messagesExpected; 88 | } 89 | else 90 | { 91 | if ((m_sessionsExpected != sessionsExpected) || 92 | (m_messagesExpected != messagesExpected)) 93 | { 94 | throw new AssertionError(); 95 | } 96 | } 97 | 98 | m_messagesReceived++; 99 | 100 | final RetainableByteBuffer reply = msg.slice(); 101 | m_session.sendData( reply ); 102 | reply.release(); 103 | 104 | msg = m_stream.getNext(); 105 | } 106 | 107 | if (m_messagesReceived == m_messagesExpected) 108 | { 109 | final long endTime = System.nanoTime(); 110 | double tm = (endTime - m_startTime) / 1000; 111 | tm /= 1000000.0; 112 | tm = (m_messagesReceived / tm); 113 | System.out.println( m_session.getRemoteAddress() + ": " + 114 | m_messagesReceived + " messages (" + 115 | m_bytesReceived + " bytes) processed at " + 116 | Util.formatDelay(m_startTime, endTime) + " sec (" + 117 | (int)tm + " msgs/sec). " ); 118 | } 119 | } 120 | 121 | public void onConnectionClosed() 122 | { 123 | System.out.println( 124 | m_session.getLocalAddress() + " -> " + m_session.getRemoteAddress() + 125 | ": connection closed" ); 126 | final int sessionsDone = m_sessionsDone.incrementAndGet(); 127 | if (sessionsDone == m_sessionsExpected) 128 | m_session.getCollider().stop(); 129 | } 130 | } 131 | 132 | private class TestAcceptor extends Acceptor 133 | { 134 | public TestAcceptor( int socketBufferSize ) 135 | { 136 | super(0); 137 | socketRecvBufSize = socketBufferSize; 138 | socketSendBufSize = socketBufferSize; 139 | } 140 | 141 | public void onAcceptorStarted( Collider collider, int localPort ) 142 | { 143 | System.out.println( "Server started at port " + localPort ); 144 | if (m_client != null) 145 | m_client.start( new InetSocketAddress("localhost", localPort) ); 146 | } 147 | 148 | public Session.Listener createSessionListener( Session session ) 149 | { 150 | return new ServerListener( session ); 151 | } 152 | } 153 | 154 | public Server( Client client ) 155 | { 156 | m_client = client; 157 | m_sessionsDone = new AtomicInteger(0); 158 | } 159 | 160 | public void run( int socketBufferSize ) 161 | { 162 | try 163 | { 164 | final Collider collider = Collider.create(); 165 | collider.addAcceptor(new TestAcceptor(socketBufferSize)); 166 | collider.run(); 167 | m_client.stopAndWait(); 168 | } 169 | catch (final IOException ex) 170 | { 171 | ex.printStackTrace(); 172 | } 173 | } 174 | } 175 | -------------------------------------------------------------------------------- /tests/src/org/jsl/tests/timer_queue/Main.java: -------------------------------------------------------------------------------- 1 | /* 2 | * JS-Collider framework tests. 3 | * Copyright (C) 2013 Sergey Zubarev 4 | * info@js-labs.org 5 | * 6 | * This program is free software: you can redistribute it and/or modify 7 | * it under the terms of the GNU Affero General Public License as 8 | * published by the Free Software Foundation, either version 3 of the 9 | * License, or (at your option) any later version. 10 | * 11 | * This program is distributed in the hope that it will be useful, 12 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 13 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 | * GNU Affero General Public License for more details. 15 | * 16 | * You should have received a copy of the GNU Affero General Public License 17 | * along with this program. If not, see . 18 | */ 19 | 20 | package org.jsl.tests.timer_queue; 21 | 22 | import org.jsl.collider.ThreadPool; 23 | import org.jsl.collider.TimerQueue; 24 | 25 | import java.util.concurrent.Semaphore; 26 | import java.util.concurrent.TimeUnit; 27 | import java.util.concurrent.atomic.AtomicInteger; 28 | 29 | public class Main 30 | { 31 | private static final long DIFF_THRESHOLD = 30; 32 | private final AtomicInteger m_done; 33 | private final Semaphore m_sema; 34 | 35 | private class Timer1 implements TimerQueue.Task 36 | { 37 | private final TimerQueue m_timerQueue; 38 | 39 | public Timer1( TimerQueue timerQueue ) 40 | { 41 | m_timerQueue = timerQueue; 42 | } 43 | 44 | public long run() 45 | { 46 | try 47 | { 48 | int rc = m_timerQueue.cancel( this ); 49 | assert( rc == -1 ); 50 | System.out.println( "Test1 [callback cancel] done." ); 51 | } 52 | catch (final InterruptedException ex) 53 | { 54 | ex.printStackTrace(); 55 | } 56 | 57 | final int done = m_done.decrementAndGet(); 58 | if (done == 0) 59 | m_sema.release(); 60 | 61 | return 0; 62 | } 63 | } 64 | 65 | private class Timer2 implements TimerQueue.Task 66 | { 67 | private final long m_interval; 68 | private long m_lastFireTime; 69 | private int m_cnt; 70 | 71 | public Timer2( long interval ) 72 | { 73 | m_interval = interval; 74 | } 75 | 76 | public long run() 77 | { 78 | /* Execution time should be first time + (cnt * period) */ 79 | final long currentTime = System.currentTimeMillis(); 80 | final int cnt = m_cnt++; 81 | 82 | if (m_lastFireTime == 0) 83 | System.out.println( "Timer2: first fire" ); 84 | else 85 | { 86 | final long diff = (currentTime - m_lastFireTime - m_interval); 87 | System.out.println( "Timer2: diff=" + diff ); 88 | if (diff > DIFF_THRESHOLD) 89 | throw new RuntimeException( "Timer2: missed too much: " + diff ); 90 | } 91 | m_lastFireTime = currentTime; 92 | 93 | if (cnt == 5) 94 | { 95 | final int done = m_done.decrementAndGet(); 96 | if (done == 0) 97 | m_sema.release(); 98 | return 0; 99 | } 100 | else 101 | return m_interval; 102 | } 103 | } 104 | 105 | private class Timer3 implements TimerQueue.Task 106 | { 107 | private final long m_interval; 108 | private long m_lastFireTime; 109 | private int m_cnt; 110 | 111 | public Timer3( long interval ) 112 | { 113 | m_interval = interval; 114 | } 115 | 116 | public long run() 117 | { 118 | /* Execution time should be first time + (cnt * period) */ 119 | final long currentTime = System.currentTimeMillis(); 120 | final int cnt = m_cnt++; 121 | 122 | if (m_lastFireTime == 0) 123 | System.out.println( "Timer3: first fire" ); 124 | else 125 | { 126 | final long diff = (currentTime - m_lastFireTime - m_interval); 127 | System.out.println( "Timer3: diff=" + diff ); 128 | if (diff > DIFF_THRESHOLD) 129 | throw new RuntimeException( "Timer3: missed too much: " + diff ); 130 | } 131 | m_lastFireTime = currentTime; 132 | 133 | if (cnt == 8) 134 | { 135 | final int done = m_done.decrementAndGet(); 136 | if (done == 0) 137 | m_sema.release(); 138 | return 0; 139 | } 140 | else 141 | return m_interval; 142 | } 143 | } 144 | 145 | private static class Timer4 implements TimerQueue.Task 146 | { 147 | public long run() 148 | { 149 | throw new RuntimeException( "Method should never be called" ); 150 | } 151 | } 152 | 153 | private Main() 154 | { 155 | m_done = new AtomicInteger(); 156 | m_sema = new Semaphore(0); 157 | } 158 | 159 | private void run() 160 | { 161 | m_done.set(3); /* 3 tests */ 162 | 163 | final ThreadPool threadPool = new ThreadPool( "TP", 4 ); 164 | threadPool.start(); 165 | 166 | final TimerQueue timerQueue = new TimerQueue( threadPool ); 167 | 168 | final Timer1 timer1 = new Timer1( timerQueue ); 169 | timerQueue.schedule( timer1, 100, TimeUnit.MILLISECONDS ); 170 | 171 | final long test2Interval = 500; 172 | final Timer2 timer2 = new Timer2( test2Interval ); 173 | timerQueue.schedule( timer2, 100, TimeUnit.MILLISECONDS ); 174 | 175 | final long test3Interval = test2Interval; 176 | final Timer3 timer3 = new Timer3( test3Interval ); 177 | timerQueue.schedule( timer3, 100, TimeUnit.MILLISECONDS ); 178 | 179 | final Timer4 timer4 = new Timer4(); 180 | timerQueue.schedule( timer4, 10, TimeUnit.SECONDS ); 181 | 182 | try 183 | { 184 | int rc = timerQueue.cancel( timer4 ); 185 | if (rc != 0) 186 | throw new RuntimeException( "Timer not canceled!" ); 187 | 188 | m_sema.acquire(); 189 | threadPool.stopAndWait(); 190 | } 191 | catch (final InterruptedException ex) 192 | { 193 | ex.printStackTrace(); 194 | } 195 | } 196 | 197 | public static void main( String [] args ) 198 | { 199 | new Main().run(); 200 | } 201 | } 202 | --------------------------------------------------------------------------------