Skip to content

Commit 8b9e93f

Browse files
committed
Some internal changes and fixes in the FSUIPC class. For example, it was not possible to start processing requests again after disconnecting/connecting again. Some cleanup was not being done.
1 parent 765b26c commit 8b9e93f

File tree

5 files changed

+136
-36
lines changed

5 files changed

+136
-36
lines changed

FSUIPC/nbproject/genfiles.properties

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,8 @@
11
build.xml.data.CRC32=2f6d8a78
22
build.xml.script.CRC32=9499e4c4
3-
build.xml.stylesheet.CRC32=f85dc8f2@1.95.0.48
3+
build.xml.stylesheet.CRC32=f85dc8f2@1.96.0.48
44
# This file is used by a NetBeans-based IDE to track changes in generated files such as build-impl.xml.
55
# Do not edit this file. You may delete it but then the IDE will never regenerate such files for you.
66
nbproject/build-impl.xml.data.CRC32=2f6d8a78
77
nbproject/build-impl.xml.script.CRC32=ab6d7d89
8-
nbproject/build-impl.xml.stylesheet.CRC32=f89f7d21@1.95.0.48
8+
nbproject/build-impl.xml.stylesheet.CRC32=f89f7d21@1.96.0.48

FSUIPC/nbproject/private/private.properties

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,4 +5,4 @@ do.jlink=false
55
javac.debug=true
66
javadoc.preview=true
77
jlink.strip=false
8-
user.properties.file=C:\\Users\\Murdock\\AppData\\Roaming\\NetBeans\\12.0\\build.properties
8+
user.properties.file=C:\\Users\\Murdock\\AppData\\Roaming\\NetBeans\\12.1\\build.properties

FSUIPC/nbproject/private/private.xml

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,8 @@
22
<project-private xmlns="http://www.netbeans.org/ns/project-private/1">
33
<editor-bookmarks xmlns="http://www.netbeans.org/ns/editor-bookmarks/2" lastBookmarkId="0"/>
44
<open-files xmlns="http://www.netbeans.org/ns/projectui-open-files/2">
5-
<group/>
5+
<group>
6+
<file>file:/P:/Java/Murdock_FSUIPC/FSUIPC/src/com/mouseviator/fsuipc/FSUIPC.java</file>
7+
</group>
68
</open-files>
79
</project-private>

FSUIPC/nbproject/project.properties

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -47,8 +47,8 @@ javac.modulepath=
4747
javac.processormodulepath=
4848
javac.processorpath=\
4949
${javac.classpath}
50-
javac.source=11
51-
javac.target=11
50+
javac.source=15
51+
javac.target=15
5252
javac.test.classpath=\
5353
${javac.classpath}:\
5454
${build.classes.dir}

FSUIPC/src/com/mouseviator/fsuipc/FSUIPC.java

Lines changed: 128 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -514,9 +514,13 @@ public static byte load64() {
514514
*/
515515
private final AtomicBoolean connected = new AtomicBoolean(false);
516516
/**
517-
* Thread pool for wait for connection task and running continuous requests
517+
* Thread pool for running continuous requests
518518
*/
519-
private final ScheduledExecutorService scheduledExecutorService = Executors.newScheduledThreadPool(2);
519+
private ScheduledExecutorService scheduledESForCRPTask = null;
520+
/**
521+
* Thread pool for wait for connection task
522+
*/
523+
private ScheduledExecutorService scheduledESForWfCTask = null;
520524
/**
521525
* A reference to task that waits for FSUIPC connection
522526
*/
@@ -537,7 +541,7 @@ public static byte load64() {
537541
*/
538542
private void checkLastResult() {
539543
int lastResult = FSUIPCWrapper.getResult();
540-
544+
541545
// in case of some error, inform all listeners
542546
if (lastResult != FSUIPCWrapper.FSUIPCResult.FSUIPC_ERR_OK.getValue()) {
543547
logger.log(Level.FINER, "Something did not go as planned! FSUIPC last result code is: {0}. Letting the listeners know!", lastResult);
@@ -589,14 +593,14 @@ private void setConnected(boolean connected) {
589593
}
590594

591595
/**
592-
* This will add listener.
596+
* This will add listener. If the listener is already present, it will not be added.
593597
*
594598
* @param listener A listener to add.
595599
* @return True if listener was added, false otherwise (you passed null, or
596600
* what {@link Collection#add(java.lang.Object) } returns.).
597601
*/
598602
public boolean addListener(IFSUIPCListener listener) {
599-
if (listener != null) {
603+
if (listener != null && !arListeners.contains(listener)) {
600604
return arListeners.add(listener);
601605
}
602606
return false;
@@ -615,6 +619,13 @@ public boolean removeListener(IFSUIPCListener listener) {
615619
}
616620
return false;
617621
}
622+
623+
/**
624+
* This will remove all listeners
625+
*/
626+
public void removeAllListeners() {
627+
arListeners.clear();
628+
}
618629

619630
/**
620631
* Open FSUIPC connection to selected simulator version.
@@ -637,15 +648,24 @@ public int connect(FSUIPCWrapper.FSUIPCSimVersion simVersion) {
637648
/**
638649
* This method will init thread that will continuously try to connect to
639650
* simulator via FSUIPC till success.
640-
*
651+
*
652+
* <strong>WARNING:</strong> This functions will first try to stop any currently running waiting thread. This might take some time - thus may
653+
* block the current thread! Should not be called from main EDT thread.
654+
*
641655
* @param simVersion A simulator version to which to connect.
642656
* @param repeatPeriod A time in seconds to repeat the connection attempts.
643657
* @return True if thread is successfully started, false otherwise.
644658
*/
645659
public boolean waitForConnection(FSUIPCWrapper.FSUIPCSimVersion simVersion, int repeatPeriod) {
646660
try {
647661
if (cancelWaitForConnectionTask()) {
648-
waitForConnectionThread = scheduledExecutorService.scheduleAtFixedRate(new WaitForConnectionWorker(simVersion), 0, repeatPeriod, TimeUnit.SECONDS);
662+
//Init task executor, if not initialized yet
663+
if (scheduledESForWfCTask == null) {
664+
//Creating thread pool with only one thread will make sure that no more than one thread will be waiting for the connection at one time
665+
scheduledESForWfCTask = Executors.newScheduledThreadPool(1);
666+
}
667+
//start waiting thread
668+
waitForConnectionThread = scheduledESForWfCTask.scheduleAtFixedRate(new WaitForConnectionWorker(simVersion), 0, repeatPeriod, TimeUnit.SECONDS);
649669
logger.log(Level.FINER, "Started new task to wait to connection to sim via FSUIPC. Required sim version is: {0} and repeat period is: {1} seoonds.", new Object[]{getFSVersion(simVersion), repeatPeriod});
650670
return true;
651671
}
@@ -665,8 +685,21 @@ private boolean cancelWaitForConnectionTask() {
665685
try {
666686
if (waitForConnectionThread != null) {
667687
//cancel any currently running task
668-
((ScheduledThreadPoolExecutor) scheduledExecutorService).setRemoveOnCancelPolicy(true);
688+
((ScheduledThreadPoolExecutor) scheduledESForWfCTask).setRemoveOnCancelPolicy(true);
669689
waitForConnectionThread.cancel(true);
690+
//wait for the task to complete
691+
while (!waitForConnectionThread.isDone()) {
692+
try {
693+
/* It is said that using Thread.sleep in the loop is bad design pattern, but here, I do not know about
694+
better solution for waiting for the thread to be completly finished.
695+
*/
696+
Thread.sleep(50);
697+
} catch (InterruptedException ex) {
698+
//just continue on processing
699+
logger.log(Level.WARNING, "Interrupted while waiting for wait for FSUIPC connection thread to finish!", ex);
700+
}
701+
}
702+
waitForConnectionThread = null; //important so that the condition above works next time
670703
logger.finer("Thread to wait for FSUIPC connection was canceled!");
671704
}
672705
} catch (Exception ex) {
@@ -679,6 +712,9 @@ private boolean cancelWaitForConnectionTask() {
679712
/**
680713
* This function will cancel the thread that is running the continual
681714
* request processing.
715+
*
716+
* <strong>WARNING:</strong> This functions will try to stop any currently running processing thread. This might take some time - thus may
717+
* block the current thread! Should not be called from main EDT thread.
682718
*
683719
* @return True if thread was canceled, false if there was some problem
684720
* (exception).
@@ -687,19 +723,30 @@ public boolean cancelRequestsProcessing() {
687723
try {
688724
if (continualRequestProcessThread != null) {
689725
//cancel any currently running task
690-
((ScheduledThreadPoolExecutor) scheduledExecutorService).setRemoveOnCancelPolicy(true);
726+
((ScheduledThreadPoolExecutor) scheduledESForCRPTask).setRemoveOnCancelPolicy(true);
691727
//this one, we will let finish if already running - the false parameter
692728
continualRequestProcessThread.cancel(false);
693729
//wait for the task to complete
694730
while (!continualRequestProcessThread.isDone()) {
695-
Thread.sleep(50);
731+
try {
732+
/* It is said that using Thread.sleep in the loop is bad design pattern, but here, I do not know about
733+
better solution for waiting for the thread to be completly finished.
734+
*/
735+
Thread.sleep(50);
736+
} catch (InterruptedException ex) {
737+
//just continue on processing
738+
logger.log(Level.WARNING, "Interrupted while waiting for continual request processing thread to finish!", ex);
739+
}
696740
}
741+
continualRequestProcessThread = null; //important so that the condition above works next time
742+
//clear array of requests
743+
arContinualRequests.clear(); //added as anothe call to start request processing in one session would add request (double them and so on)
697744
logger.finer("Thread for FSUIPC continual requests processing was canceled!");
698745
}
699746
} catch (Exception ex) {
700747
logger.log(Level.SEVERE, "Failed to cancel thread for FSUIPC continual requests processing!", ex);
701748
return false;
702-
}
749+
}
703750
return true;
704751
}
705752

@@ -710,19 +757,57 @@ public void disconnect() {
710757
logger.info("Called disconnect! Will close FSUIPC connection and release resoures.");
711758
setConnected(false);
712759

713-
//cancel runnig threads if any
714-
cancelWaitForConnectionTask();
760+
//cancel runnig threads waiting for FSUIPC connection if any
761+
if (scheduledESForWfCTask != null) {
762+
try {
763+
scheduledESForWfCTask.shutdown();
764+
while (!scheduledESForWfCTask.isShutdown()) {
765+
//wait for tasks to shutdown
766+
try {
767+
/* It is said that using Thread.sleep in the loop is bad design pattern, but here, I do not know about
768+
better solution for waiting for the executor to be completly shutdown. We could call awaitTermination,
769+
but it has tim limit and if tasks does not finish within limit, than we still have to take another action,
770+
so may not be sure the executor finished all and was shutdown
771+
*/
772+
Thread.sleep(50);
773+
} catch (InterruptedException ex) {
774+
//just continue on processing
775+
logger.log(Level.WARNING, "Interrupted while waiting for \"Wait for FSUIPC connection\" task executor to shutdown!", ex);
776+
}
777+
}
778+
scheduledESForWfCTask = null;
779+
waitForConnectionThread = null;
780+
logger.finer("The \"Wait for FSUIPC connection\" task executor is shutdown. Thread waiting for connection is terminated.");
781+
} catch (Exception ex) {
782+
logger.log(Level.SEVERE, "Failed to terminate the \"Wait for FSUIPC connection\" task executor! The waiting thread might still be running!", ex);
783+
}
784+
}
715785

716-
//terminate all running threads
717-
try {
718-
scheduledExecutorService.shutdown();
719-
while (!scheduledExecutorService.isShutdown()) {
720-
//wait for tasks to shutdown
721-
Thread.sleep(50);
786+
//terminate all running continual request processing threads
787+
if (scheduledESForCRPTask != null) {
788+
try {
789+
scheduledESForCRPTask.shutdown();
790+
while (!scheduledESForCRPTask.isShutdown()) {
791+
//wait for tasks to shutdown
792+
try {
793+
/* It is said that using Thread.sleep in the loop is bad design pattern, but here, I do not know about
794+
better solution for waiting for the executor to be completly shutdown. We could call awaitTermination,
795+
but it has tim limit and if tasks does not finish within limit, than we still have to take another action,
796+
so may not be sure the executor finished all and was shutdown
797+
*/
798+
Thread.sleep(50);
799+
} catch (InterruptedException ex) {
800+
//just continue on processing
801+
logger.log(Level.WARNING, "Interrupted while waiting for \"Continual requests processing\" scheduled task executor to shutdown!", ex);
802+
}
803+
}
804+
scheduledESForCRPTask = null;
805+
continualRequestProcessThread = null;
806+
arContinualRequests.clear(); //added as another call to start request processing in one session would add request (double them and so on)
807+
logger.finer("The \"Continual requests processing\" task executor is shutdown. Thread performing continual request processing is terminated.");
808+
} catch (Exception ex) {
809+
logger.log(Level.SEVERE, "Failed to terminate the \"Continual requests processing\" executor service! The thread performing continual request processing might still be running!", ex);
722810
}
723-
logger.finer("All scheduled threads stopped and executor service terminated.");
724-
} catch (Exception ex) {
725-
logger.log(Level.SEVERE, "Failed to terminate all scheduled threads and to stop executor service!", ex);
726811
}
727812

728813
//close FSUIPC connection. We do it as last command, after cancelling all processing, as doing it as firts, may
@@ -819,15 +904,15 @@ public String getVersion() {
819904

820905
/**
821906
* Returns string representation of FSUIPC library version. Calls {@link FSUIPCWrapper#getVersion()
822-
* } to get FSUIPC version as number first. Note that this version number is hard-coded in FSUIPC C lib
823-
* and was not updated for years :)
907+
* } to get FSUIPC version as number first. Note that this version number is
908+
* hard-coded in FSUIPC C lib and was not updated for years :)
824909
*
825910
* @return String representation of FSUIPC library version.
826911
*/
827912
public String getLibVersion() {
828913
//According to documentation the values hould be stored the same as the fsuipc vesrion, but it does not give meaningfull value
829-
int version = FSUIPCWrapper.getLibVersion();
830-
914+
int version = FSUIPCWrapper.getLibVersion();
915+
831916
return String.format("%.3f", version / 1000.f);
832917
}
833918

@@ -953,8 +1038,8 @@ private int process() {
9531038
//now, process all
9541039
int iRet = FSUIPCWrapper.process();
9551040
//compute elapsed time
956-
lastProcessingTime = System.nanoTime() - startTime;
957-
1041+
lastProcessingTime = System.nanoTime() - startTime;
1042+
9581043
//return value based od process result
9591044
if (iRet == 0) {
9601045
checkLastResult(); //check whether we are still connected
@@ -1003,6 +1088,9 @@ public int processRequestsOnce() {
10031088
* functions. If processing is completed without errors, the "one time"
10041089
* requests array will be cleared.
10051090
*
1091+
* <strong>WARNING:</strong> This function will try to stop any currently running processing thread (if cancelRunning is true). This might take some time - thus may
1092+
* block the current thread! Should not be called from main EDT thread.
1093+
*
10061094
* @param repeatPeriod How often to process the requests. Milliseconds.
10071095
* @param cancelRunning Whether to cancel the task if currently running and
10081096
* start a new one.
@@ -1030,8 +1118,18 @@ public int processRequests(int repeatPeriod, boolean cancelRunning) {
10301118
}
10311119

10321120
try {
1121+
//init task executor if not initialized yet
1122+
if (scheduledESForCRPTask == null) {
1123+
//Creating thread pool with only one thread will make sure that no more than one thread will be processing requests at one time
1124+
//thus, listener functions that are called from processing thread will have to be completed before the thread can run next time
1125+
//Hope this will prevent data incosistency that might would occur when the executor would run the same processing code again
1126+
//before the completion of previous code (which could happen with more than one thread in pool). This would cause data issues where
1127+
//data in instances that are read by FSUIPC would be overwritten before listener has time to process them, as FSUIPC lib writes the changes
1128+
//directly into the respective variable memory
1129+
scheduledESForCRPTask = Executors.newScheduledThreadPool(1);
1130+
}
10331131
//start our process continual request thread
1034-
continualRequestProcessThread = scheduledExecutorService.scheduleAtFixedRate(new ContinualRequestsProcessWorker(), 0, repeatPeriod, TimeUnit.MILLISECONDS);
1132+
continualRequestProcessThread = scheduledESForCRPTask.scheduleAtFixedRate(new ContinualRequestsProcessWorker(), 0, repeatPeriod, TimeUnit.MILLISECONDS);
10351133
logger.log(Level.FINER, "Started thread to process continual requests at period of: {0} miliseconds.", repeatPeriod);
10361134
} catch (Exception ex) {
10371135
logger.log(Level.SEVERE, "Failed to start thread to process continual requests!", ex);
@@ -1096,7 +1194,7 @@ public void run() {
10961194
registerRequests(arOneTimeRequests);
10971195
registerRequests(arContinualRequests);
10981196

1099-
int iRet = process();
1197+
int iRet = process();
11001198
//clear the one time requests
11011199
if (iRet == PROCESS_RESULT_OK) {
11021200
arOneTimeRequests.clear();

0 commit comments

Comments
 (0)