• Post Reply Bookmark Topic Watch Topic
  • New Topic
programming forums Java Mobile Certification Databases Caching Books Engineering Micro Controllers OS Languages Paradigms IDEs Build Tools Frameworks Application Servers Open Source This Site Careers Other Pie Elite all forums
this forum made possible by our volunteer staff, including ...
Marshals:
  • Campbell Ritchie
  • Ron McLeod
  • Rob Spoor
  • Tim Cooke
  • Junilu Lacar
Sheriffs:
  • Henry Wong
  • Liutauras Vilda
  • Jeanne Boyarsky
Saloon Keepers:
  • Jesse Silverman
  • Tim Holloway
  • Stephan van Hulst
  • Tim Moores
  • Carey Brown
Bartenders:
  • Al Hobbs
  • Mikalai Zaikin
  • Piet Souris

Avoiding Stack overflow during recursion

 
Ranch Hand
Posts: 52
  • Mark post as helpful
  • send pies
    Number of slices to send:
    Optional 'thank-you' note:
  • Quote
  • Report post to moderator
Hello all,

I have a hardware test routine that runs recursively. In the recursive sequence, method calls continued infinitely to initiate hardware changes over serial. The code is something like this:



The code works well and is very tidy. But, of course, after a few weeks of this running 24/7, I encounter a stack overflow. Is there any formal way to guard against this?

Thanks!
 
Saloon Keeper
Posts: 8589
71
Eclipse IDE Firefox Browser MySQL Database VI Editor Java Windows
  • Likes 1
  • Mark post as helpful
  • send pies
    Number of slices to send:
    Optional 'thank-you' note:
  • Quote
  • Report post to moderator
Why recursive and not infinite loop ?
 
Marshal
Posts: 74050
332
  • Likes 1
  • Mark post as helpful
  • send pies
    Number of slices to send:
    Optional 'thank-you' note:
  • Quote
  • Report post to moderator
The usual way to implement recursion is for every call to be slightly “smaller” than its predecessor; eventually you will get such a small call that you can regard it as “zero” and that stops the recusion.
Why would you want an infinite loop or recursion?
 
Saloon Keeper
Posts: 1319
40
Eclipse IDE Postgres Database C++ Java
  • Mark post as helpful
  • send pies
    Number of slices to send:
    Optional 'thank-you' note:
  • Quote
  • Report post to moderator
I have worked places where they had poorly designed code with special needs that recursed very, very deeply.
We had to compile our C/C++ code with huge stack limits so it wouldn't crash.

However, it wasn't infinite, just larger than the defaults set by the compilers which assumed normal usage.

I remember reading stuff about performance, everything about the hardware we were running on presumed stack depths much lower than we needed to run with, but again, this was "Deep Call Stacks", not, like, infinite.

I agree that normal recursion terminates in some logical place.  If it was very large this would be a problem on small embedded devices, but everywhere else you should be able to adjust to large but finite values.

Again, not infinite though.

from our friend the Javadocs:
-Xss size
Sets the thread stack size (in bytes). Append the letter k or K to indicate KB, m or M to indicate MB, or g or G to indicate GB. The default value depends on the platform:

Linux/x64 (64-bit): 1024 KB

macOS (64-bit): 1024 KB

Oracle Solaris (64-bit): 1024 KB

Windows: The default value depends on virtual memory

The following examples set the thread stack size to 1024 KB in different units:

-Xss1m
-Xss1024k
-Xss1048576
This option is similar to -XX:ThreadStackSize.

-XX:ThreadStackSize=size
Sets the Java thread stack size (in kilobytes). Use of a scaling suffix, such as k, results in the scaling of the kilobytes value so that -XX:ThreadStackSize=1k sets the Java thread stack size??to 1024*1024 bytes or 1 megabyte. The default value depends on the platform:

Linux/x64 (64-bit): 1024 KB

macOS (64-bit): 1024 KB

Oracle Solaris (64-bit): 1024 KB

Windows: The default value depends on virtual memory

The following examples show how to set the thread stack size to 1 megabyte in different units:

-XX:ThreadStackSize=1k
-XX:ThreadStackSize=1024
This option is similar to -Xss.


No option for infinity, apparently.
 
Saloon Keeper
Posts: 24325
167
Android Eclipse IDE Tomcat Server Redhat Java Linux
  • Likes 1
  • Mark post as helpful
  • send pies
    Number of slices to send:
    Optional 'thank-you' note:
  • Quote
  • Report post to moderator

Simon McNamara wrote:The code works well and is very tidy. But, of course, after a few weeks of this running 24/7, I encounter a stack overflow. Is there any formal way to guard against this?



Yes, if you can't find a suitable "de-recursion" condition, make one. Create a depth counter and every time you recurse, increment it. Return when the count exceeds your desired limit.

But if this is supposed to be an "infinitely-running" job, recursion does not sound like the right approach. As Carey pointed out, this sounds more like a job for an infinite loop than for recursion.
 
Simon McNamara
Ranch Hand
Posts: 52
  • Mark post as helpful
  • send pies
    Number of slices to send:
    Optional 'thank-you' note:
  • Quote
  • Report post to moderator
Hey all,

Thanks for the feedback. Yes, I recognize that there is usually a break condition. This program is a hardware "life" test. We literally run this stuff until it dies!

I suppose an infinite loop would work. If I change the code to:



Won't I still eventually overload the stack? Or is that only happening on method calls?

Return when the count exceeds your desired limit.



Why does it matter if I return?

Why not this?:

 
Carey Brown
Saloon Keeper
Posts: 8589
71
Eclipse IDE Firefox Browser MySQL Database VI Editor Java Windows
  • Likes 2
  • Mark post as helpful
  • send pies
    Number of slices to send:
    Optional 'thank-you' note:
  • Quote
  • Report post to moderator
The first example code leaves nothing on the stack, the second example will eat a little of the stack away with each round of recursion (as you found out).
 
Campbell Ritchie
Marshal
Posts: 74050
332
  • Likes 2
  • Mark post as helpful
  • send pies
    Number of slices to send:
    Optional 'thank-you' note:
  • Quote
  • Report post to moderator
To expand what Carey said: the loop example reuses the same area of stack space repeatedly and the second example with explicit recursion expands into fresh stack memory with every call . . . until you run out of anywhere to put the method calls.
 
Jesse Silverman
Saloon Keeper
Posts: 1319
40
Eclipse IDE Postgres Database C++ Java
  • Mark post as helpful
  • send pies
    Number of slices to send:
    Optional 'thank-you' note:
  • Quote
  • Report post to moderator
When do I use recursion for something that "sounds like" what you were doing?

Three things apply:
1. I am gradually building up something finite, but unknown in depth/length that needs to be considered , used or evaluated at each depth of recursion or iteration.
2. I need to do "back-tracking", using the stack makes it very easy to know "where I am" at any moment without writing any fancy code.
3. It isn't easy to do the back-tracking by hand, i.e. whatever I am building up isn't easy to see how to reverse, because I would need to "remember" what I added on each iteration or recursion and therefore needed to "back out" while back-tracking.  The stack gives me this for free!

If those don't apply, I will normally do things iteratively, I am not consuming stack because it doesn't "buy me anything".

When I gave my first answer, I was answering as if they DID apply to the OP's situation and I just didn't see it yet, but still covered "Do you really need recursion at all here?" case too.

When you DO need recursion (or it would be very hard to go without it for the three reasons cited) and the stack can necessarily get very deep, my original answer would still apply.
 
Simon McNamara
Ranch Hand
Posts: 52
  • Mark post as helpful
  • send pies
    Number of slices to send:
    Optional 'thank-you' note:
  • Quote
  • Report post to moderator
Is the following code at risk of stack overflow, even though it's not recursive? It also has an infinite series of method calls, but they're not infinitely deep.

 
Carey Brown
Saloon Keeper
Posts: 8589
71
Eclipse IDE Firefox Browser MySQL Database VI Editor Java Windows
  • Mark post as helpful
  • send pies
    Number of slices to send:
    Optional 'thank-you' note:
  • Quote
  • Report post to moderator
At least with the code you've shown there should be no risk of overflow.
 
Carey Brown
Saloon Keeper
Posts: 8589
71
Eclipse IDE Firefox Browser MySQL Database VI Editor Java Windows
  • Mark post as helpful
  • send pies
    Number of slices to send:
    Optional 'thank-you' note:
  • Quote
  • Report post to moderator
I smell a hint of "polling" in what you've sketched out. Polling is an infinite loop that keeps asking the system to do something and them immediately asks it to do it again, on infinitum. It can be very inefficient and resource intensive. Also, when it's doing overTheRiver() and can't be doing throughTheWoods() and vice versa.

It looks like you may have some asynchronous stuff going on like the serial communication mentioned. Taking a dive into the deep end of the pool it would appear that you need at least two asynchronous Threads that can block on serial IO.
 
Jesse Silverman
Saloon Keeper
Posts: 1319
40
Eclipse IDE Postgres Database C++ Java
  • Mark post as helpful
  • send pies
    Number of slices to send:
    Optional 'thank-you' note:
  • Quote
  • Report post to moderator
It is legal, but a bit stylistically odd to do all the work for a class in its constructor.

Carey just responded with the rest of what I was going to say, but better.
 
Simon McNamara
Ranch Hand
Posts: 52
  • Mark post as helpful
  • send pies
    Number of slices to send:
    Optional 'thank-you' note:
  • Quote
  • Report post to moderator
I suppose I am polling. However, that's the nature of the life test. I tell the hardware to do one thing. It does that for a while, then I tell it do something else. Then the whole thing repeats.

Each piece of the sequence is not outright running at maximum speed, though. I use a TimerTask to pace data acquisition and file printing. I think perhaps that gets at the problem of "polling". I have something like this:



I'm open to any suggestions of course. Many thanks for the feedback.
 
Saloon Keeper
Posts: 13280
292
  • Likes 1
  • Mark post as helpful
  • send pies
    Number of slices to send:
    Optional 'thank-you' note:
  • Quote
  • Report post to moderator
NEVER trigger execution of a task on another thread from a constructor. Add a start() method that you call AFTER your constructor has finished running. Otherwise, it is not guaranteed that the parallel task will observe the object to be in a fully initialized state, which can be a source of very insidious bugs.

Constructors really should not do much more work than just initialize the object's fields.

Where did you declare sampleCount?

Why are you passing a TimerTask to an Executor? You can implement Runnable instead of extending TimerTask.

Why are you using a low level concurrency primitive like Semaphore, instead of Java's high level concurrency classes, such as ReentrantLock? And why are you acquiring the lock only once but releasing it many times? And what critical section of your code is the lock trying to protect in the first place?

It's questionable to call Object.wait() regardless of why you're doing it, but calling it on your executor service makes no sense and is probably not going to work the way you intended it to.

Please don't put multiple statements on a single line. Format your try-statement properly by putting the code in the try-block and the catch-block on their own lines.

Use identifiers that properly describe what the method is doing. overTheRiver() and throughTheWoods() are nice names for a toy example, but use real names for real code.

Please describe the problem you're trying to solve in more detail, and we might suggest some sample code.
 
Simon McNamara
Ranch Hand
Posts: 52
  • Mark post as helpful
  • send pies
    Number of slices to send:
    Optional 'thank-you' note:
  • Quote
  • Report post to moderator
Hey Stephan. Thanks for the feedback. I think some of your questions about my code could be answered by adding a more complete example. I didn't want to bog down my example with too many details. Instead it seems I've wasted everyone's time by posting code which is too incomplete. Sorry about that. This class runs as the back end of a javafx GUI, so I extend Task:



Stephan van Hulst wrote:Please describe the problem you're trying to solve in more detail, and we might suggest some sample code.



This code is a life test routine for LED die mounted to PCB test boards. The boards are wired to LED drivers capable of illuminating individual die. We commonly use libraries in java to talk to the LED drivers over serial (JSSC) or directly to the driver board microcontroller over Modbus (JLibModbus). We pulse the die On/Off at a given frequency, duty cycle, and current. For some tests we instead leave the die on continuously. In between this main sequence, we run other routines to check the health of individual die. During the "point check" sequence, we illuminate each individual die one-at-a-time to ensure they're not dead. All light intensity measurements are recorded on simple diode photosensors. Data is printed during these routines.

Stephan van Hulst wrote:NEVER trigger execution of a task on another thread from a constructor. Add a start() method that you call AFTER your constructor has finished running. Otherwise, it is not guaranteed that the parallel task will observe the object to be in a fully initialized state, which can be a source of very insidious bugs.



I think this updated code addresses this point. I'm glad you mention this though, it's really by chance I set it up this way!

Stephan van Hulst wrote:Why are you passing a TimerTask to an Executor? You can implement Runnable instead of extending TimerTask.



I suppose this class can extends Task and implements Runnable. Since TimerTask implements Runnable, perhaps I could have a TimerTask method instead of a nested inner class? I'm not totally sure what that gets me, but this had not occurred to me.

Stephan van Hulst wrote:Why are you using a low level concurrency primitive like Semaphore, instead of Java's high level concurrency classes, such as ReentrantLock? And why are you acquiring the lock only once but releasing it many times? And what critical section of your code is the lock trying to protect in the first place?



I think I am acquring and releasing it many times. Each time mainPulseSequence is called I acquire the lock. It is only released after the routine inside of TimerTask finishes. I kept Semaphore because it worked. I tried using this.wait() on the Task Thread instead of a Semaphore object, but then nothing I tried could wake it again from the nested TimeTask thread. I thought it was normal to acquire the lock and release it over and over again, especially since mainPulseSequence may run thousands of times.

Stephan van Hulst wrote:It's questionable to call Object.wait() regardless of why you're doing it, but calling it on your executor service makes no sense and is probably not going to work the way you intended it to.



I tested this code with simple print statements to the console. When I don't add ScheduledThreadPoolExecutor.wait, weird things would happen. It seemed like the thread kept running despite cancelling the TimerTask.

Thanks for your feedback, I really appreciate your help.
 
Campbell Ritchie
Marshal
Posts: 74050
332
  • Mark post as helpful
  • send pies
    Number of slices to send:
    Optional 'thank-you' note:
  • Quote
  • Report post to moderator
You won't get your code to compile with Void as a return type because Void can't be instantiated. I think you mean void. Don't start method names with Capital Letters; the @Override annotation will catch Call().
Stephan has advised you to use a Lock rather than a Semaphore. Why have you get an Executor as a field of the Task?
 
Simon McNamara
Ranch Hand
Posts: 52
  • Mark post as helpful
  • send pies
    Number of slices to send:
    Optional 'thank-you' note:
  • Quote
  • Report post to moderator
I'm not completely sure why, but Void is the correct type in this case. I already compiled the code from the prior post. Thanks for catching Call, didn't notice I capitalized that. The compiler certainly does not like that.

I pass the timer task to the Executor, which seems to be normal for ScheduledThreadPoolExecutor#scheduleAtFixedRate, shown here: https://docs.oracle.com/javase/7/docs/api/java/util/concurrent/ScheduledExecutorService.html

I obviously need to do some reading on both Semaphore and Lock before I settle on one. Many thanks for pointing me in the right direction!
 
Campbell Ritchie
Marshal
Posts: 74050
332
  • Mark post as helpful
  • send pies
    Number of slices to send:
    Optional 'thank-you' note:
  • Quote
  • Report post to moderator
Where is the call() method inherited from? Maybe I am mistaken, but I am I think you shouldn't use Callable#call() without some return value. You cannot return a Void because Void is uninstantiable. The nearest you can get to it is ...return null;
 
Marshal
Posts: 22453
121
Eclipse IDE Spring VI Editor Chrome Java Windows
  • Mark post as helpful
  • send pies
    Number of slices to send:
    Optional 'thank-you' note:
  • Quote
  • Report post to moderator

Campbell Ritchie wrote:You won't get your code to compile with Void as a return type because Void can't be instantiated. I think you mean void.


You're almost correct. There is one possible return value for Void: null. That's a good thing, because using Void as generic type for a function interface that returns something (like Callable, but I've encountered several others as well) isn't that uncommon if you don't need to actually return anything.
 
Campbell Ritchie
Marshal
Posts: 74050
332
  • Mark post as helpful
  • send pies
    Number of slices to send:
    Optional 'thank-you' note:
  • Quote
  • Report post to moderator
Thank you, Rob.
 
Rob Spoor
Marshal
Posts: 22453
121
Eclipse IDE Spring VI Editor Chrome Java Windows
  • Mark post as helpful
  • send pies
    Number of slices to send:
    Optional 'thank-you' note:
  • Quote
  • Report post to moderator
You're welcome.
 
Stephan van Hulst
Saloon Keeper
Posts: 13280
292
  • Mark post as helpful
  • send pies
    Number of slices to send:
    Optional 'thank-you' note:
  • Quote
  • Report post to moderator
Hi Simon, I took a look at your updated code, and there are a couple of comments I can make about it that I haven't before.

  • Don't name your class FilePrinter if it's primary purpose is to collect samples from the LED. A name like LedSampleCollector seems more descriptive.
  • Make your classes final unless you have a REALLY good reason to extend them.
  • Don't let your services create their own executor services. Pass executors into your FilePrinter through the constructor. This applies to all services that your classes depend on, not just executors.
  • Map is an interface. You can't use it in an instance creation expression unless you're defining an anonymous class.
  • Make all your fields private. The outside world has no business accessing them directly or at all.
  • Again, for emphasis, NEVER trigger threads from constructors. In your previous code it was done indirectly. In your updated could you are literally calling Thread.start(). DON'T.
  • Why are you using threads at all if you have an executor service? Newly written code rarely has any good reason to reference the Thread class at all.
  • Keep your business layer completely separated from your presentation layer. Your sample collector shouldn't know about the front-end, and therefore has nothing to do with JavaFX. Either let it implement Runnable or Callable (and let an external class submit it to an executor service) or don't let it implement anything, and let it submit its own tasks to the executor service. You can easily chain asynchronous operations using the CompletableFuture class.
  • Why does the code that calls mainPulseSequence() and pointCheckSequence() run asynchronously if you're directly going to let it wait on the background tasks spawned in those methods anyway? Just perform the sequences synchronously.

  • Simon McNamara wrote:I think I am acquring and releasing it many times. Each time mainPulseSequence is called I acquire the lock. It is only released after the routine inside of TimerTask finishes.


    Yes, but in a single call of mainPulseSequence() you call scheduleWithFixedDelay() (as opposed to schedule()), which causes your task to run many times. Calling cancel() will do nothing, because you're not using Timer to run the TimerTask. I presume that this is the reason that you're calling Object.wait(). But this is NOT doing what you think or hope it is doing, even if it appears to work. Don't use wait().

    The root of the problem is that you're mixing TimerTask with ScheduledExecutorService. Just call ScheduledExecutorService.schedule() with a custom Runnable (NOT TimerTask) and let it run to its end. You don't need to cancel it if you run it only once.

    I tried using this.wait() on the Task Thread instead of a Semaphore object, but then nothing I tried could wake it again from the nested TimeTask thread.


    Again, wait() does not do what you think it does. It does NOT suspend the task or executor that you call it on. Instead, it's similar to calling lock.acquire(), with the object that you're calling it on acting as a lock.

    I'll see if I can write some example code later that approximates what you want to achieve.
     
    Stephan van Hulst
    Saloon Keeper
    Posts: 13280
    292
    • Mark post as helpful
    • send pies
      Number of slices to send:
      Optional 'thank-you' note:
    • Quote
    • Report post to moderator
    Before I can give you a good example, you need to explain carefully to me why the code inside the TimerTask needs to run asynchronously with respect to the code inside the Call() method, seeing as that code is already running asynchronously with respect to the front-end.
     
    Simon McNamara
    Ranch Hand
    Posts: 52
    • Mark post as helpful
    • send pies
      Number of slices to send:
      Optional 'thank-you' note:
    • Quote
    • Report post to moderator
    Apologies for the late reply, busy week!

    Stephan van Hulst wrote:Before I can give you a good example, you need to explain carefully to me why the code inside the TimerTask needs to run asynchronously with respect to the code inside the Call() method, seeing as that code is already running asynchronously with respect to the front-end.



    That code does not strictly need to run asynchronously. It must run at a fixed delay.

    Don't let your services create their own executor services. Pass executors into your FilePrinter through the constructor. This applies to all services that your classes depend on, not just executors.



    I'm not clear why a service is any different from any other object. Why not just make them as needed and throw them away? I'm thinking it probably has something to do with what the java docs say about Interface ExecutorService, "An Executor that provides methods to manage termination and methods that can produce a Future for tracking progress of one or more asynchronous tasks." I suppose of the services are all necessarily the same between objects from the GUI, I can keep them synchronized more easily?

    ExecutorService: https://docs.oracle.com/javase/7/docs/api/java/util/concurrent/ExecutorService.html

    Again, for emphasis, NEVER trigger threads from constructors. In your previous code it was done indirectly. In your updated could you are literally calling Thread.start(). DON'T.



    I suppose I can start the thread from the GUI itself, where the Task is created, but why not put it in the constructor. I can't see the risk. A class which extends Task isn't running concurrently until I call Task#call, so there's no risk of the constructor not finishing before other operations begin (as far as I know).

    The root of the problem is that you're mixing TimerTask with ScheduledExecutorService. Just call ScheduledExecutorService.schedule() with a custom Runnable (NOT TimerTask) and let it run to its end. You don't need to cancel it if you run it only once.



    Thanks Stephan, I will look into ScheduledExecutorService. From the cursory peek just now I see that it is syntactically similar to ScheduledThreadPoolExecutor. Looks like I pass the former a Task whereas I pass the latter a Runnable. I'll study this a bit.

    Again, wait() does not do what you think it does. It does NOT suspend the task or executor that you call it on. Instead, it's similar to calling lock.acquire(), with the object that you're calling it on acting as a lock.



    Even if I passed in services from the GUI thread, I would still need to pause them at times. How would I do this without #wait? Lock#acquire is pretty much exactly the behavior I want, so I don't see the conflict.

    Many thanks for the replies. I appreciate your time and effort.


     
    Stephan van Hulst
    Saloon Keeper
    Posts: 13280
    292
    • Mark post as helpful
    • send pies
      Number of slices to send:
      Optional 'thank-you' note:
    • Quote
    • Report post to moderator

    Simon McNamara wrote:That code does not strictly need to run asynchronously. It must run at a fixed delay.


    When you say "at a fixed delay", must it run continuously with a fixed delay between two test sequences, or must it run only once after a certain delay after a button is pushed? From your questions, I gather that your user interface has a button that starts execution of the tests, and keeps running them continuously until you pause them with another button click.

    I'm not clear why a service is any different from any other object. Why not just make them as needed and throw them away?


    I like to subdivide classes into roughly two categories: Value types and services. If a class doesn't represent a fixed value, it provides a service to other classes.

    Classes should have a single responsibility. If a class creates a service that it depends on itself, from the perspective of the outside world that class then does two things: It performs its own responsibilities and it also appears as if it's also doing the things that its internal service is doing. Let me give you a concrete example of why this matters.

    When I test a class that depends on an executor service, I want to create the executor service myself so that I can let it run under tightly controlled parameters. I will inject it into the class I'm testing, and then I know for sure that I'm really testing that class' responsibilities, and I don't have to worry about whatever services that class is setting up for itself.

    I suppose I can start the thread from the GUI itself, where the Task is created, but why not put it in the constructor. I can't see the risk. A class which extends Task isn't running concurrently until I call Task#call, so there's no risk of the constructor not finishing before other operations begin (as far as I know).


    This is wrong. Background tasks don't start running when you call run() or call(). If you call run() or call(), the current thread will run those tasks synchronously. This poses no danger at all, but it's also pointless: Why even create a background task when you're going to let them run synchronously?

    Background tasks start when you call start() on a thread, or when you submit the background task to an executor service. This is exactly what you're doing in your FilePrinter constructor. You pass this to the Thread, and then you call Thread.start(). You must never do this. This is one of the most insidious causes of bugs in Java.

    I'll explain why it is dangerous: Multithreaded execution is a very weird and confusing place. Java allows processors to execute commands in a different order than the order they appear in the code, as long as the final result is the same from the perspective of the thread that is executing the code. For other threads, it may appear that commands are executed in random order, unless you perform careful synchronization. In your code it probably won't do much harm because thread.start() is at the end of the constructor, so there isn't any code left to rearrange, but if somebody were to extend the FilePrinter class, they might start seeing really weird bugs, where fields of the object appear to be uninitialized even though you were certain that the initializers have already run.

    If you want, I can give you a very detailed explanation of Java's memory model so that it becomes more obvious why starting background tasks in constructors is dangerous. Let me know and I'll write a separate post about it. At any rate, never start threads from constructors. This includes calling methods that do so indirectly, or submitting background tasks to executor services. There is a super easy workaround:

    Instead of:

    Do:


    I will look into ScheduledExecutorService. From the cursory peek just now I see that it is syntactically similar to ScheduledThreadPoolExecutor. Looks like I pass the former a Task whereas I pass the latter a Runnable.


    It shouldn't be surprising that they're similar. ScheduledExecutorService is an interface that is implemented by ScheduledThreadPoolExecutor. Even though you initialize your mainSequenceThreadExecutor with a ScheduledThreadPoolExecutor, you don't need to declare it as one, because ScheduledExecutorService exposes all the methods that you need.

    Neither of these two types refer to any type call Task. The reason you can submit a JavaFX task is because Task implements Runnable.

    Even if I passed in services from the GUI thread, I would still need to pause them at times. How would I do this without #wait? Lock#acquire is pretty much exactly the behavior I want, so I don't see the conflict.


    Object.wait() is old, unreliable and difficult to use. It is largely superseded by ReentrantLock and Condition. However, you would not use ReentrantLock to prevent a task from being scheduled. Just cancel the task, and reschedule it when you want the tests to continue. You were on the right track when you called cancel(), but it didn't work because you were mixing TimerTask and ScheduledExecutorService, which manage the background threads differently.

    The solution is not to use Object.wait(), or Semaphore.acquire() or ReentrantLock.lock() or Condition.await(), because these block the thread and prevent it from performing other tasks while your test sequences are paused. The solution is to call cancel() on the ScheduledFuture that is returned from the ScheduledExecutorService when you call scheduleWithFixedDelay().


    You can call startTestSequences() and stopTestSequences() from your frontend's action handler very easily, and those calls will return very quickly and let the main pulse sequence and point check sequence run in the background repeatedly.
     
    Campbell Ritchie
    Marshal
    Posts: 74050
    332
    • Mark post as helpful
    • send pies
      Number of slices to send:
      Optional 'thank-you' note:
    • Quote
    • Report post to moderator

    Simon McNamara wrote:. . . I suppose I can start the thread from the GUI itself

    Careful: GUIs are usually not thread‑safe. I think you may be thinking you go from GUI to application, but you should usually go from application to GUI.

    . . . there's no risk of the constructor not finishing before other operations begin (as far as I know). . . .

    “Famous Last Words”
     
    Ranch Hand
    Posts: 104
    • Mark post as helpful
    • send pies
      Number of slices to send:
      Optional 'thank-you' note:
    • Quote
    • Report post to moderator
    Although this thread had become very technical very fast, including threads and concurrency and such, I would like to provide a simple answer which may not be what the OP was originally asking but may answer the question posed in the title of the post. Maybe someone will find it of some use.

    1. The only way to truly ensure avoiding stack overflow using recursion in Java is not to use recursion. This can be done by converting your algorithm to an imperative style using loops instead of recursion.
    2. Not all recursive algorithms can be converted to loops, period.
    3. If your algorithm is tail-recursive, you can convert to using a loop without using additional space, thereby avoiding the stack overflow issue.
    4. Otherwise, you might consider implementing your own stack (in the heap). If you do this, you may still run into the problem of overflowing your own stack (in the heap).
     
    With a little knowledge, a cast iron skillet is non-stick and lasts a lifetime.
    reply
      Bookmark Topic Watch Topic
    • New Topic