I created a GUI Application in JavaFX which communicates with a serial device (I use a jssc connector). When I want to get data I send a communicate, then wait 1s until the wait function terminates. Communicate is also being sent, among other things, when I click one of the buttons (for starting a device, for identification, etc.). During development process I noticed a bug - communication hangs (but only when receiving messages, I still can send one-way communicates for ie. starting a device) when I click too many buttons too fast or I click buttons which send more than one communicate.
Communication is mainly handled by my own class SerialPortDevice. I create an object of a class' type and then invoke particular methods. Here's the method which waits for a message:
private String waitForMessage() throws SerialPortException {
long operationStartTime = System.currentTimeMillis();
long connectionTimeout = SerialPortCommunicationParameters.CONNECTION_TIMEOUT_IN_MILLIS;
String resultMessage = "";
do {
if (readEventOccurred) {
System.out.println();
resultMessage = receiveMessage();
System.out.println("After receiving a message");
messageReceived = true;
}
} while (((System.currentTimeMillis() - operationStartTime) < connectionTimeout) && (!messageReceived));
if (!readEventOccurred) {
resultMessage = NO_RESPONSE;
}
System.out.println("Time elapsed: " + (System.currentTimeMillis() - operationStartTime + "ms"));
return resultMessage;
}
One can notice that message is being received only when a flag readEventOccured is true. It's handled by my implementation of the SerialPortEventListener:
class SerialPortDeviceReader implements SerialPortEventListener {
private SerialPortDevice device;
SerialPortDeviceReader(SerialPortDevice device) {
this.device = device;
}
public void serialEvent(SerialPortEvent event) {
if (event.isRXCHAR()) {
System.out.println("Serial Event Occured!");
device.setReadEventOccurred(true);
}
}
}
readEventOccured is a boolean field in the SerialPortDevice class which contains waitForMessage function. Also, waitForMessage is invoked by another function, singleConversation:
String singleConversation(String testMessage) {
String resultMessage = NO_RESPONSE;
try {
openConnection();
sendMessage(testMessage);
resultMessage = waitForMessage();
closeConnection();
} catch (SerialPortException e) {
e.printStackTrace();
return resultMessage;
}
System.out.println();
readEventOccurred = false;
messageReceived = false;
return resultMessage;
}
... Which is the only function setting readEventOccured to false. And it is a "top level" function in a SerialPortDevice class which handles sending and receiving a communicates to and from a device.
So communication looks like this:
Button click -> Button handler invocation -> device.singleCommunication(buttons_specific_communicate) -> some methods run and then it comes to the waitForMessage -> method waits 1s for the event -> event occurs (everytime - I get the "Serial Event Occured" communicate) -> readEventOccured is being set to true -> if there is some time left (there is always some time left, everything lasts a miliseconds), message is being received in the waitForMessage method.
There is no problem if I click buttons with a short (in a human sense, 2-3s for example) delay or I don't click these which do send more than one communicate inside their handlers. In different situations weird things happen. I still get the message "Serial Event Occured" (so I suppose readEventOccured is also being set to true) but waitForMessage function does not execute
if(readEventOccured)
statement's code. Also, I have to run an application again to be able to communicate with a device (I mean receiving data, sending works perfectly).
What solved my problem was adding "volatile" modifier to the readEventOccured flag (makes sense by the way, things go fast sometimes). But it didn't please me. I wanted to make a code to run correctly without "volatile". My coworker came up with an idea something goes wrong with threads that are being created when I click buttons and invoke communication - maybe something blocks other thing somewhere? First thing I did was to print all the currently runnig threads and... Man, it solved a problem. App was not hanging anymore. Really, I performed "hanging" scenarios 10-20 times with and without
Set<Thread> threadSet = Thread.getAllStackTraces().keySet();
System.out.println(threadSet);
at the beginning of the waitForMessage method and result were unambigous - it somehow removed my problem.
I'm almost certainly sure getting and printing threads is not a solution itself. It's something that is being done by the way but I can't find out what is this. Any useful information? Maybe better understanding Threads in Java would help me? Or it is something else?
Cheers
What you're doing is the textbook example of what happens when there are no visibility guarantees. If we distil your code into its essential bits, we get something like this:
boolean flag = false;
void consume() {
while (true) {
if (flag) {
//we received flag, hooray
}
}
}
void produce() {
flag = true;
}
Now, if produce() and consume() run in different threads, there is absolutely no guarantee that consume() will EVER see flag set to true. volatile creates a memory barrier, meaning that all reads/writes of those variable will be fully ordered.
What you also have in the code though, is lots of System.out.println() lines. These complicate the picture, as they themselves are synchronized, and thus create happens-before relationships between different parts of the code. Unfortunately they don't create the correct sequence, but poke in a few more and you can accidentally get it right. The key word here is "accidentally", you shouldn't rely on this behaviour at all.
So marking readEventOccurred as volatile sort of fixes the problem but going a bit further we can see that your waitForMessage() spin-waits, which is rarely a good idea. I would take a look at the CountDownLatch class for example, designed for similar scenarios. (An even better candidate is its close friend, CyclicBarrier.)
What solved my problem was adding "volatile" modifier to the readEventOccured flag (makes sense by the way, things go fast sometimes). But it didn't please me. I wanted to make a code to run correctly without "volatile".
The fact that adding volatile fixes the problem indicates you've run into the lack of guarantees by the Java Memory Model when multiple threads are involved. Simply put there is no guarantee of when a change on one thread is visible on other threads except for specific situations.
The probably reason why printing 'fixes' the problem is the fact that it:
Changes the timing of the program, might mean the situation in which it fails no longer exists.
Temporarily suspends the threads to read out their current state, maybe the JVM also uses that as an opportunity to synchronize memory.
Multithreaded communication is hard, so I recommend you take a look at the classes available in java.util.concurrent, which have guarantees that you might be able to use to solve your problem more elegantly.
Related
I am making something similar to minecraft plugin, but the API is all in Java.
At some point, there are events. Every event is fired sequentially, and the API will wait until an event finishes before calling the event listener for the next event. These events are called from the main thread, this means that nothing else is running while your event listener code is being run, and the application will crash eventually if you are blocking the main thread for too long.
Basically, I want that when a player is opening an inventory, some first content is sent directly, and some second content is retrieved from a database. In Java, you, would do something like this:
private static final List<Item> firstContent = ...;
public static void onInventoryOpenEvent(Inventory inventory) {
inventory.addItems(firstContent);
forkJoinPool.submit(() -> { // "fire and forget"
List<Item> secondContent = retrieveContentFromDB(); // long and blocking
inventory.addItems(secondContent);
});
}
And in kotlin, some would say to use the GlobalScope, others would say to not use it, but for now I don't see any other way than using it like this:
fun onInventoryOpenEvent(inventory: Inventory) { // not a suspending function
inventory.addItems(firstContent)
GlobalScope.launch {
val secondContent = retrieveContentFromDB() // long and blocking
inventory.addItems(secondContent)
}
}
If you truly want to fire and forget, such that the coroutine will not be cancelled under any circumstance, GlobalScope is the right way to do it. The need to truly fire and forget is rare enough in practice that Kotlin gives you a warning about a "fragile API" if you use it, I think because so many newbies were using it for everything. This was especially a problem on Android (where Kotlin is primarily used), where long running tasks that should run even off-screen should be handled in services instead of coroutines.
But, referring to your example, if inventory is something that could become obsolete and you want to free it to the GC at some point, you should use your own created CoroutineScope that you store in a property, so you can cancel it to clear any running coroutines and prevent them from hanging onto references that should be freed for the GC. If the scope might be used for more than one coroutine, then you should give it a SupervisorJob().
private val coroutineScope = CoroutineScope(SupervisorJob())
fun onEndOfLifecycle() { // some function called when cleaning up memory
coroutineScope.cancel()
}
Guys how to deal with such code and warning?
private void listenOnLogForResult() {
String logs = "";
int timeCounter = 1;
while (logs.isEmpty()) {
try {
timeCounter++;
Thread.sleep(2000); // Wait 2 seconds
} catch (InterruptedException e) {
log.error(e.getLocalizedMessage(), e);
}
if (timeCounter < 30) {
logs = checkLogs()
} else {
logs = "Time out";
}
}
}
I need to pause current thread for 2 seconds to wait file to be filled, but my Intelij Rise an issue here.
And also I am getting error from sonar:
SonarLint: Either re-interrupt this method or rethrow the "InterruptedException".
I've tried already with many ExecutorService, but it is always run in seperate thread, and I need to pause current one.
Please help..
The busy-waiting warning
This is a warning coming from intellij that is dubious, in the sense that what you're doing is often just straight up required. In other words, it is detecting a pattern that is overused, but whose usage cannot be reduced to 0. So, likely the right solution is to just tell intellij to shut up about it here.
The problem it is looking at is not that Thread.sleep. That is not the problem. However, intellij's detector of this pattern needs it to find this case, but it is not what it is complaining about, which might be a little hard to wrap your head around.
What IntelliJ is worried about, is that you're wasting cycles continually rechecking log.isEmpty() for no reason. It has a problem with the while aspect of this code, not the sleep. It would prefer to see code where you invoke some sort of logs.poll() method which will just wait until it is actively woken up by the act of new logs appearing.
If this is all running within a single java process, then you can indeed rewrite this entire system (which includes rewrites to whatever log is here, and a complete re-imagining of the checkLogs() method: Instead of going out and checking, whatever is making logs needs to wake up this code instead.
If it's not, it is likely that you need to tell intellij to shut it: What you are doing is unavoidable without a complete systems redesign.
The re-interrupt warning
You have some deplorable exception handling here.
Your exception handling in general
Do not write catch blocks that log something and keep moving. This is really bad error handling: The system's variables and fields are now in an unknown state (you just caught and logged some stuff: Surely that means you have no idea what conditions have occurred to cause this line of execution to happen!), and yet code will move right along. It is extremely likely that 'catch exceptions and just keep going' style code results in more exceptions down the line: Generally, code that operates on unknown state is going to crash and burn sooner rather than later.
Then, if that crash-and-burn is dealt with in the same fashion (catch it, log it, keep going), then you get another crash-and-burn. You end up with code that will, upon hitting a problem, print 186 exceptions to the log and they are all utterly irrelevant except the first one. That's bad yuyu.
You're also making it completely impossible for calling code to recover. The point of exceptions is that they need to bubble upwards endlessly: Either the exception is caught by code that actually knows how to deal with the problem (and logging it is not dealing with it!), which you are making impossible, or, the code exception should bubble up all the way to the entry-point handler which is the right place to log the error and abort the entry-point handler.
An entry-point handler is a generic module or application runner; out of the box, the code baked into java.exe itself that ends up invoking your psv main() method is the most obvious 'entry point runner', but there's more: Web frameworks will eventually invoke some code of yours that is supposed to handle a web request: That code of yours is analogous to psv main(): It is the entry-point, and the code in the web framework that invokes it, is the entry-point runner.
Entry-point runners have a good reason to catch (Throwable t), and to spend their catch block primarily logging it, though they should generally log a lot more than just the exception (a web handler should for example log the request details, such as which HTTP params were sent and which path request it was, maybe the headers, etc). Any other code should never do this, though.
If you have no idea what to do and don't want to think about what that exception might mean, the correct 'whatever, just compile already javac' code strategy is to add the exception type to your throws line. If that is not feasible, the right code in the catch block is:
} catch (ExceptionIDoNotWantToThinkAboutRightNow e) {
throw new RuntimeException("Uncaught", e);
}
This will ensure that code will not just merrily continue onwards, operating on unknown state, and will ensure you get complete details in logs, and ensures that calling code can catch and deal with it if it can, and ensures that any custom logging info such as the HTTP request details get a chance to make it to the logs. Win-win-win-win.
This case in particular: What does InterruptedEx mean?
When some code running in that java process invokes yourThread.interrupt(), that is how InterruptedException can happen, and it cannot possibly happen in any other way. If the user hits CTRL+C, or goes into task manager and clicks 'end process', or if your android phone decides it is time for your app to get out as the memory is needed for something else - none of those cases can possibly result in InterruptedExceptions. Your threads just get killed midstep by java (if you want to act on shutdowns, use Runtime.getRuntime().addShutdownHook). The only way is for some code to call .interrupt(), and nothing in the core libs is going to do that. Thus, InterruptedException means whatever you think 'call .interrupt() on this thread' means. It is up to you.
The most common definition is effectively 'I ask you to stop': Just shut down the thread nicely. Generally it is bad to try to shut down threads nicely if you want to exit the entire VM (just invoke System.shutdown - you already need to deal with users hitting CTRL+C, why write shutdown code twice in different ways?) - but sometimes you just want one thread to stop. So, usually the best code to put in a catch (InterruptedException e) block is just return; and nothing else. Don't log anything: The 'interrupt' is intentional: You wrote it. Most likely that is nowhere in your code base and the InterruptedException is moot: It won't ever happen.
In your specific code, what happens if your code decides to stop the logger thread is that the logger thread will log something to the error logs, and will then shortcut its 2 second wait period to immediately check the logs, and then just keeps going. That sounds completely useless.
But, it means whatever you want it to. If you want an ability for e.g. the user to hit a 'force check the logs right now' button, then you can define that interrupting the logging thread just shortcuts the 2 seconds (but then just have an empty catch block with a comment explaining that this is how you designed it, obviously don't log it). If you ALSO want a button to 'stop the logging thread', have an AtomicBoolean that tracks 'running' state: When the 'stop log-refreshes' button is hit, set the AB to 'false' and then interrupt the thread: Then the code you pasted needs to check the AB and return; to close the thread if it is false.
fun sleep(timeMillis: Long) {
val currentTimeMillis = System.currentTimeMillis()
while (true) {
if (System.currentTimeMillis() - currentTimeMillis >= timeMillis) {
break
}
}
}
and use this in your method(It's code by koltin,you should trans to java)
We have a server that does backpressure in http1.1 (yes, it turns off sockets and clients actually freeze up waiting if we are under load...it's quite nice). We do it in http2 as well without the http2 specification's doing backpressure in the http2 engine(ie. we don't need an backpressure there but we follow the spec and it also turns off sockets). When I say 'turn off', I only mean it deregisters it so we stop reading the nic and our nic buffer fills and then clients nic fills until he freezes.
Anyways, we ran into an interesting issue and have been trying to solve it without a memory leak. Our latest iteration was something like this
public CompletableFuture<Void> runLoop(List<T> newData, Session session, Processor<T> processFunction) {
//All the below futures must be chained with previous ones in case previous ones are not
//done which will serialize it all to be in sequence
CompletableFuture<Void> future = session.getProcessFuture();
for(T data : newData) {
//VERY IMPORTANT: Writing the code like this would slam through calling process N times
//BUT it doesn't give the clients a chance to set a flag between packets
//Mainly done for exceptions and streaming so you can log exc, set a boolean so you
//don't get 100 exceptions while something is happening like socket disconnect
//In these 2 lines of code, processCorrectly is CALLED N times RIGHT NOW
//The code below this only calls them right now IF AND ONLY IF the client returns
//a completed future each time!!!
//CompletableFuture<Void> messageFuture = processFunction.process(data);
//future = future.thenCompose( f -> messageFuture);
future = future.thenCompose( voidd -> processFunction.process(data));
}
session.setProcessFuturee(future);
return future;
}
As data comes in runLoop is called for N data chunks. runLoop is always called serially so we have no race conditions there. We call runLoop X times. The issue is that in the current code above processFunction.process MAY be called on a different thread (and in fact is quite frequently for our streaming endpoints only).
I am wondering how to cut the cord so to speak so that we stop chain. commenting out session.setProcessFuture DOES stop the chain but the issue there is incoming data that comes in 2nd can then beat the data that came in first(and has but rarely).
The test I am using which if we comment out setProcessFuture bounces between 500MB and 6MB of memory on and off is the link below. If I don't comment that out, it slowly uses up all 500MB.
https://github.com/deanhiller/webpieces/blob/master/core/core-util/src/test/java/org/webpieces/util/futures/TestLoopingChainMemory.java
Trying to not have the race condition and not have memory issues and also allow backpressure which is done using the future that we return from runLoop(). If there are too many unresolved, we stop feeding in traffic to runLoop.
FYI: thenCompose == scala's flatMap
EDIT: I had another idea and so I tried this but this failed as well. The List size is typically pretty small in production so I didn't mind stacking up the futures in the loop but trying to cut the chain outside the loop to prevent the memory from adding up over time and never being cleaned...
public CompletableFuture<Void> runLoop(List<T> newData, Session session, Processor<T> processFunction) {
//All the below futures must be chained with previous ones in case previous ones are not
//done which will serialize it all to be in sequence
CompletableFuture<Void> future = session.getProcessFuture();
CompletableFuture<Void> newFuture = new CompletableFuture<Void>();
for(T data : newData) {
//VERY IMPORTANT: Writing the code like this would slam through calling process N times
//BUT it doesn't give the clients a chance to seet a flag between packets
//Mainly done for exceptions and streaming so you can log exc, set a boolean so you
//don't get 100 exceptions while something is happening like socket disconnect
//In these 2 lines of code, processCorrectly is CALLED N times RIGHT NOW
//The code below this only calls them right now IF AND ONLY IF the client returns
//a completed future each time!!!
//This seems to have memory issues as well....
//CompletableFuture<Void> temp = processFunction.process(data);
//future = future.thenCompose(f -> temp);
future = future.thenCompose( voidd -> processFunction.process(data));
}
future.handle((voidd, t) -> {
if(t != null) {
newFuture.completeExceptionally(t);
return 0;
}
newFuture.complete(null);
return 0;
});
//comment this out and memory leak goes away of course.......
session.setProcessFuturee(newFuture);
return newFuture;
}
EDIT: ok, I discovered that this line helps BUT I had to put code in to wait much longer for memory to cleanup. It took a 'long' time to clean up and it memory goes down to 13MB. Now, I am wondering why cleanup is taking so long...perhaps objects made it through to older generations in the gc model
future = future.thenComposeAsync( voidd -> processFunction.process(data), executor );
THEN, I realized what if wait on my original code. This is where things got weird. It only went back down to 196MB and stayed there. I am not sure why or what the reference is. I really don't see anything in MAT(eclipse) or I am using that tool wrong.
Do I have a memory leak? I am confused on this last result. it should go down to < 20MB at least
EDIT(addressing Shadov's answer): I believe the future returned is not chained to where it came from. It is the 'tail' of the chain so to speak I think. It's the opposite though, the future that created it is tied to that so that when the future that created it is done, it can resolve that future. So in my mind, I am posting the tail of the list in the session each time(not creating a chain that is longer and longer). The real issue with futures is the thread that references it and whether it was resolved. This means the only one left referencing it is the anonymous Runnable that resolves it. Once that happens, it should be able to resolve.
CompletableFuture is not even herein memory (MAT eclipse)..
OMG, another major discovery.. If I comment out this line
future.complete(null);
then the program flies forever with memory bouncing between 180MB and 6MB.
Not really good at this, but you are building bigger and bigger future and never stopping - no matter the size of the list, it's only gonna consume more and more memory, never releasing it.
You will see if you do in your test:
chain.runLoop(list, s, p);
if(queue.size() == 0) {
System.out.println("queue empty");
s.setProcessFuturee(CompletableFuture.completedFuture(null));
rt.gc();
}
So in my opinion you need to use a different tool, CompletableFuture may be not strong enough for this. Maybe some full fledged reactive library, like rxjava or reactor?
Ok, I did a git push and git commit. There is no memory leak. What is happening is that we slam the main thread so hard, it all accumulates........when giving time stuff to resolve, memory goes back to 6MB. It just takes VERY long time but the logs helped see that easier AND I most importantly was missing the runtime.gc() call in a critical spot!!!!
soooo, with backpressure in place, we should have no issues since that makes all the futures resolves before adding load.
When I need to do an indeterminate number of pieces of work in the JavaFX thread without blocking the user interface, I use this class
public class AsyncWhile {
private final IntPredicate hook;
private int schedCount = 0;
private boolean terminated = false;
private int callCount = 0;
private static final int schedN = 1;
public AsyncWhile(IntPredicate hook) {
this.hook = hook;
schedule();
}
public void kill(){
terminated = true;
}
private void schedule(){
while(schedCount < schedN){
Platform.runLater(this::poll);
schedCount++;
}
}
private void poll(){
schedCount--;
if(!terminated){
terminated = !hook.test(callCount++);
if(!terminated){
schedule();
}
}
}
}
like this
asyncWhile = new AsyncWhile(i -> {
// return false when you're done
// or true if you want to be called again
});
// can asyncWhile.kill() should we need to
(
If you need a more concrete example, here I'm reading one line at a time from an InputStream and then parsing and displaying a plot parsed from that line:
asyncWhile = new AsyncWhile(i -> {
String line;
try {
if((line = reader.readLine()).startsWith(" Search complete.")){ // it so happens that this reader must be read in the JavaFX thread, because it automatically updates a console window
return false;
} else {
Task<MatchPlot> task = new ParsePlotTask(line);
task.setOnSucceeded(wse -> {
plotConsumer.accept(task.getValue());
// todo update progress bar
});
executorService.submit(task);
return true;
}
} catch (IOException ex) {
new ExceptionDialog(ex).showAndWait();
return false;
}
});
)
Chaining up runLaters like that feels like a hack. What is the proper way to solve this kind of problem? (By "this kind of problem" I mean the problem that would have been solved by a simple while loop, had it not been for the fact that its contents must run in the JavaFX thread without making the UI unresponsive.)
Recommended
In general, basing a solution off of the PartialResultsTask sample from the Task documentation (which relies on Platform.runLater invocations), is the standard way of solving this problem.
Alternate
Rather than scheduling runLater's you could use a BlockingDeque. In your processing task, you perform your time-consuming process just with a normal while loop, generate non-UI model objects which need to be represented in the JavaFX UI, stick those non-UI model objects into your queue. Then you setup a Timeline or AnimationTimer that polls the queue, draining it as necessary and to pick the items off the queue and represent them in the UI.
This approach is similar (but a bit different) to: Most efficient way to log messages to JavaFX TextArea via threads with simple custom logging frameworks.
Using your own queue in this case is not much different from using the implicit queue runLater invocations go on to, though, with your own queue, you might have a little more control over the process if you need that. It's a trade-off though, as it adds a bit more custom code and complexity, so probably just use the recommended PartialResults sample from Task and, if that doesn't fit your needs, then perhaps investigate the alternative custom queue based approach.
Aside
As a side note, you could use the custom logging framework linked earlier to log console messages from multiple threads to be displayed in your UI. That way you don't need to have your reader.readLine call execute I/O on the JavaFX UI, which is not recommended. Instead, have the I/O performed off the JavaFX UI thread and, as you process items, call into the logging framework to log messages that will eventually show up on the UI (the internal mechanisms within the logging framework take care of ensuring that JavaFX threading rules are respected).
Can you see any danger in using my approach?
Sorry for being non-specific here. I'm not going to directly answer this, but tangentially and not always applicably to your approach, using runLater can cause issues, mostly it is not a concern, but some things to consider:
If you send enough runLater calls faster than they can be processed, eventually you will either run out of memory or some runLater calls will start being ignored (depending on how the runLater system works).
Calls to runLater are sequential, not prioritized, so if there are internal events which are also being runLater, such as handling UI events, those might be delayed while your runLater calls are being processed.
runLater offers no guarantee of when later is. If your work is time sensitive, that might be an issue or at least something you need to account for in your implementation.
The runLater system is likely internally fairly complex and you won't know exactly how it is implemented unless you study the source code pretty closely.
Anything that you run on runLater is going to hold up the JavaFX application thread, probably until all of the outstanding runLater calls are complete
Once you have issued a bunch of runLater calls, you can't easily intersperse their processing over multiple pulses in the JavaFX animation system, they will likely all be executed on the next pulse. So you have to be careful not to send too many calls at once.
Those are just some things that come to mind.
In general though, runLater is a sound mechanism for many tasks and a core part of the JavaFX architecture. For most things the above considerations don't really have any consequence.
Writing quality multi-threaded code is pretty tricky. To the point where it often best avoided where possible, which is what the JavaFX system attempts to do for the most part by making scene graph access single-threaded. If you must do it, then stick to the patterns outlined in the Task documentation or utilizing some of the high level java.util.concurrent systems as much as possible rather than implementing your own systems. Also note that reading multi-threaded code is even trickier than writing it, so make sure what you do is clear to the next person.
I am currently trying to write a very simple chat application to introduce myself to java socket programming and multithreading. It consists of 2 modules, a psuedo-server and a psuedo-client, however my design has lead me to believe that I'm trying to implement an impossible concept.
The Server
The server waits on localhost port 4000 for a connection, and when it receives one, it starts 2 threads, a listener thread and a speaker thread. The speaker thread constantly waits for user input to the console, and sends it to the client when it receives said input. The listener thread blocks to the ObjectInputStream of the socket for any messages sent by the client, and then prints the message to the console.
The Client
The client connects the user to the server on port 4000, and then starts 2 threads, a listener and s speaker. These threads have the same functionality as the server's threads, but, for obvious reasons, handle input/output in the opposite way.
The First Problem
The problem I am running into is that in order to end the chat, a user must type "Bye". Now, since my threads have been looped to block for input:
while(connected()){
//block for input
//do something with this input
//determine if the connection still exists (was the message "Bye"?)
}
Then it becomes a really interesting scenario when trying to exit the application. If the client types "Bye", then it returns the sending thread and the thread that listened for the "Bye" on the server also returns. This leaves us with the problem that the client-side listener and the server-side speaker do not know that "Bye" has been typed, and thus continue execution.
I resolved this issue by creating a class Synchronizer that holds a boolean variable that both threads access in a synchronized manner:
public class Synchronizer {
boolean chatting;
public Synchronizer(){
chatting = true;
onChatStatusChanged();
}
synchronized void stopChatting(){
chatting = false;
onChatStatusChanged();
}
synchronized boolean chatting(){
return chatting;
}
public void onChatStatusChanged(){
System.out.println("Chat status changed!: " + chatting);
}
}
I then passed the same instance of this class into the thread as it was created. There was still one issue though.
The Second Problem
This is where I deduced that what I am trying to do is impossible using the methods I am currently employing. Given that one user has to type "Bye" to exit the chat, the other 2 threads that aren't being utilized still go on to pass the check for a connection and begin blocking for I/O. While they are blocking, the original 2 threads realize that the connection has been terminated, but even though they change the boolean value, the other 2 threads have already passed the check, and are already blocking for I/O.
This means that even though you will terminate the thread on the next iteration of the loop, you will still be trying to receive input from the other threads that have been properly terminated. This lead me to my final conclusion and question.
My Question
Is it possible to asynchronously receive and send data in the manner which I am trying to do? (2 threads per client/server that both block for I/O) Or must I send a heartbeat every few milliseconds back and forth between the server and client that requests for any new data and use this heartbeat to determine a disconnect?
The problem seems to reside in the fact that my threads are blocking for I/O before they realize that the partner thread has disconnected. This leads to the main issue, how would you then asynchronously stop a thread blocking for I/O?
I feel as though this is something that should be able to be done as the behavior is seen throughout social media.
Any clarification or advice would be greatly appreciated!
I don't know Java, but if it has threads, the ability to invoke functions on threads, and the ability to kill threads, then even if it doesn't have tasks, you can add tasks, which is all you need to start building your own ASync interface.
For that matter, if you can kill threads, then the exiting threads could just kill the other threads.
Also, a "Bye" (or some other code) should be sent in any case where the window is closing and the connection is open - If Java has Events, and the window you're using has a Close event, then that's the place to put it.
Alternately, you could test for a valid/open window, and send the "Bye" if the window is invalid/closed. Think of that like a poor mans' event handler.
Also, make sure you know how to (and have permission to) manually add exceptions to your networks' firewall(s).
Also, always test it over a live network. Just because it works in a loopback, doesn't mean it'll work over the network. Although you probably already know that.
Just to clarify for anyone who might stumble upon this post in the future, I ended up solving this problem by tweaking the syntax of my threads a bit. First of all, I had to remove my old threads, and replace them with AsyncSender and AsyncReader, respectively. These threads constantly send and receive regardless of user input. When there is no user input, it simply sends/receives a blank string and only prints it to the console if it is anything but a blank string.
The Workaround
try{
if((obj = in.readObject()) != null){
if(obj instanceof String)
output = (String) obj;
if(output.equalsIgnoreCase("Bye"))
s.stop();
}
}
catch(ClassNotFoundException e){
e.printStackTrace();
}
catch(IOException e){
e.printStackTrace();
}
In this iteration of the receiver thread, it does not block for input, but rather tests if the object read was null (no object was in the stream). The same is done in the sender thread.
This successfully bypasses the problem of having to stop a thread that is blocking for I/O.
Note that there are still other ways to work around this issue, such as using the InterruptableChannel.