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 |
--------------------------------------------------------------------------------