Skip to content
Closed
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
62 changes: 33 additions & 29 deletions server-session/src/main/java/com/iluwatar/sessionserver/App.java
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@
import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;
import java.util.concurrent.*;
import lombok.extern.slf4j.Slf4j;

/**
Expand All @@ -57,6 +58,9 @@ public class App {
private static Map<String, Instant> sessionCreationTimes = new HashMap<>();
private static final long SESSION_EXPIRATION_TIME = 10000;

private static final ScheduledExecutorService scheduler =
Executors.newSingleThreadScheduledExecutor(Thread.ofVirtual().factory());

/**
* Main entry point.
*
Expand All @@ -78,39 +82,39 @@ public static void main(String[] args) throws IOException {
sessionExpirationTask();

LOGGER.info("Server started. Listening on port 8080...");

Runtime.getRuntime().addShutdownHook(new Thread(() -> {
LOGGER.info("Shutting down scheduler...");
scheduler.shutdown();
}));
}

private static void sessionExpirationTask() {
new Thread(
() -> {
while (true) {
try {
LOGGER.info("Session expiration checker started...");
Thread.sleep(SESSION_EXPIRATION_TIME); // Sleep for expiration time
Instant currentTime = Instant.now();
synchronized (sessions) {
synchronized (sessionCreationTimes) {
Iterator<Map.Entry<String, Instant>> iterator =
sessionCreationTimes.entrySet().iterator();
while (iterator.hasNext()) {
Map.Entry<String, Instant> entry = iterator.next();
if (entry
.getValue()
.plusMillis(SESSION_EXPIRATION_TIME)
.isBefore(currentTime)) {
sessions.remove(entry.getKey());
iterator.remove();
}
}
}
scheduler
.scheduleAtFixedRate(() -> {
try {
LOGGER.info("Session expiration checker started...");
Instant currentTime = Instant.now();
synchronized (sessions) {
synchronized (sessionCreationTimes) {
Iterator<Map.Entry<String, Instant>> iterator =
sessionCreationTimes.entrySet().iterator();
while (iterator.hasNext()) {
Map.Entry<String, Instant> entry = iterator.next();
if (entry
.getValue()
.plusMillis(SESSION_EXPIRATION_TIME)
.isBefore(currentTime)) {
sessions.remove(entry.getKey());
iterator.remove();
}
LOGGER.info("Session expiration checker finished!");
} catch (InterruptedException e) {
LOGGER.error("An error occurred: ", e);
Thread.currentThread().interrupt();
}
}
})
.start();
}
LOGGER.info("Session expiration checker finished!");
} catch (Exception e) {
LOGGER.error("An error occurred: ", e);
}
}, SESSION_EXPIRATION_TIME, SESSION_EXPIRATION_TIME, TimeUnit.MILLISECONDS);
}
Comment on lines 95 to 122
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Replaced busy-wait loop with a scheduled task that periodically checks for expired sessions. The code uses a fixed-rate schedule with a 10s interval and safely removes expired sessions under synchronized access to shared maps.

}
}
Comment on lines +97 to +123
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The session expiration task is now scheduled with a fixed-rate executor. It eliminates the busy-wait loop in main and uses a synchronized block around the two maps. Potential deadlock risk exists due to nested synchronization on separate locks; consider consolidating to a single lock or using a concurrent map approach to avoid nested locks.

Loading