result = new CompletableFuture<>();
55 | lock.lock();
56 | try {
57 | try {
58 | String logStatement;
59 | if (!pendingPolls.isEmpty()) {
60 | pendingPolls.offer(result);
61 | } else if ((logStatement = pollNow()) != null) {
62 | return logStatement;
63 | } else {
64 | pendingPolls.offer(result);
65 | }
66 | } finally {
67 | lock.unlock();
68 | }
69 | return result.get(10, TimeUnit.SECONDS);
70 | } catch (InterruptedException | ExecutionException | TimeoutException e) {
71 | throw new RuntimeException(e);
72 | }
73 | }
74 |
75 | private String pollNow() {
76 | if (!queue.isEmpty()) {
77 | for (final Process earliest : queue.firstEntry().getValue()) {
78 | if (earliest.getEndTime() != -1) {
79 | queue.firstEntry().getValue().remove(earliest);
80 | if (queue.firstEntry().getValue().isEmpty()) {
81 | queue.pollFirstEntry();
82 | }
83 | map.remove(earliest.getId());
84 | final var logStatement = "task " + earliest.getId() + " started at: " + earliest.getStartTime() + " and ended at: " + earliest.getEndTime();
85 | System.out.println(logStatement);
86 | return logStatement;
87 | }
88 | }
89 | }
90 | return null;
91 | }
92 | }
93 |
--------------------------------------------------------------------------------
/java-projects/src/main/java/logger/LogClient.java:
--------------------------------------------------------------------------------
1 | package logger;
2 |
3 | import java.util.*;
4 | import java.util.concurrent.*;
5 | import java.util.concurrent.locks.Lock;
6 | import java.util.concurrent.locks.ReentrantLock;
7 |
8 | public interface LogClient {
9 | /**
10 | * When a process starts, it calls 'start' with processId.
11 | */
12 | void start(String processId, long timestamp);
13 |
14 | /**
15 | * When the same process ends, it calls 'end' with processId.
16 | */
17 | void end(String processId);
18 |
19 | /**
20 | * Polls the first log entry of a completed process sorted by the start time of processes in the below format
21 | * {processId} started at {startTime} and ended at {endTime}
22 | *
23 | * process id = 1 --> 12, 15
24 | * process id = 2 --> 8, 12
25 | * process id = 3 --> 7, 19
26 | *
27 | * {3} started at {7} and ended at {19}
28 | * {2} started at {8} and ended at {12}
29 | * {1} started at {12} and ended at {15}
30 | */
31 | String poll();
32 | }
33 |
34 | class LoggerImplementation implements LogClient {
35 |
36 | private final Map processes;
37 | private final ConcurrentSkipListMap queue;
38 | private final List> futures;
39 | private final Lock lock;
40 | private final ExecutorService[] taskScheduler;
41 |
42 | public LoggerImplementation() {
43 | this.processes = new ConcurrentHashMap<>();
44 | this.queue = new ConcurrentSkipListMap<>();
45 | this.futures = new CopyOnWriteArrayList<>();
46 | this.lock = new ReentrantLock();
47 | this.taskScheduler = new ExecutorService[10];
48 | for (int i = 0; i < taskScheduler.length; i++) {
49 | taskScheduler[i] = Executors.newSingleThreadExecutor();
50 | }
51 | }
52 |
53 | @Override
54 | public void start(String processId, long timestamp) { //1
55 | taskScheduler[processId.hashCode() % taskScheduler.length].execute(() -> {
56 | final Process process = new Process(processId, timestamp);
57 | processes.put(processId, process);
58 | queue.put(timestamp, process);
59 | });
60 | }
61 |
62 | @Override
63 | public void end(String processId) { //1
64 | taskScheduler[processId.hashCode() % taskScheduler.length].execute(() -> {
65 | lock.lock();//1
66 | try {
67 | final long now = System.currentTimeMillis();//1
68 | processes.get(processId).setEndTime(now);//1
69 | if (!futures.isEmpty() && queue.firstEntry().getValue().getId().equals(processId)) {
70 | pollNow();
71 | final var result = futures.remove(0);
72 | result.complete(null);
73 | }
74 | } finally {
75 | lock.unlock();
76 | }
77 | });
78 | }
79 |
80 | @Override
81 | public String poll() {
82 | lock.lock();
83 | try {
84 | final var result = new CompletableFuture();
85 | if (!queue.isEmpty() && queue.firstEntry().getValue().getEndTime() != -1) {
86 | pollNow();
87 | } else {
88 | futures.add(result);
89 | }
90 | try {
91 | result.get(3, TimeUnit.SECONDS);
92 | } catch (InterruptedException | ExecutionException | TimeoutException e) {
93 | throw new RuntimeException(e);
94 | }
95 | return null;
96 | } finally {
97 | lock.unlock();
98 | }
99 | }
100 |
101 | private String pollNow() {
102 | final Process process = queue.firstEntry().getValue();
103 | final var logStatement = process.getId() + " started at " + process.getStartTime() + " and ended at " + process.getEndTime();
104 | System.out.println(logStatement);
105 | processes.remove(process.getId());
106 | queue.pollFirstEntry();
107 | return logStatement;
108 | }
109 | }
110 |
111 | class Process {
112 | private final String id;
113 | private final long startTime;
114 | private long endTime;
115 |
116 | public Process(final String id, final long startTime) {
117 | this.id = id;
118 | this.startTime = startTime;
119 | endTime = -1;
120 | }
121 |
122 | public String getId() {
123 | return id;
124 | }
125 |
126 | public long getStartTime() {
127 | return startTime;
128 | }
129 |
130 | public long getEndTime() {
131 | return endTime;
132 | }
133 |
134 | public void setEndTime(long endTime) {
135 | this.endTime = endTime;
136 | }
137 | }
138 |
139 | class LoggerMain {
140 | /*
141 | * {3} started at {7} and ended at {19}
142 | * {2} started at {8} and ended at {12}
143 | * {1} started at {12} and ended at {15}
144 | */
145 | public static void main(String[] args) {
146 | final LogClient logger = new LoggerImplementation();
147 | logger.start("1", 1);
148 | logger.poll();
149 | logger.start("3", 2);
150 | logger.poll();
151 | logger.end("1");
152 | logger.poll();
153 | logger.start("2", 3);
154 | logger.poll();
155 | logger.end("2");
156 | logger.poll();
157 | logger.end("3");
158 | logger.poll();
159 | logger.poll();
160 | logger.poll();
161 | //1
162 | //3
163 | //2
164 | }
165 | }
--------------------------------------------------------------------------------
/.idea/uiDesigner.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | -
6 |
7 |
8 | -
9 |
10 |
11 | -
12 |
13 |
14 | -
15 |
16 |
17 | -
18 |
19 |
20 |
21 |
22 |
23 | -
24 |
25 |
26 |
27 |
28 |
29 | -
30 |
31 |
32 |
33 |
34 |
35 | -
36 |
37 |
38 |
39 |
40 |
41 | -
42 |
43 |
44 |
45 |
46 | -
47 |
48 |
49 |
50 |
51 | -
52 |
53 |
54 |
55 |
56 | -
57 |
58 |
59 |
60 |
61 | -
62 |
63 |
64 |
65 |
66 | -
67 |
68 |
69 |
70 |
71 | -
72 |
73 |
74 | -
75 |
76 |
77 |
78 |
79 | -
80 |
81 |
82 |
83 |
84 | -
85 |
86 |
87 |
88 |
89 | -
90 |
91 |
92 |
93 |
94 | -
95 |
96 |
97 |
98 |
99 | -
100 |
101 |
102 | -
103 |
104 |
105 | -
106 |
107 |
108 | -
109 |
110 |
111 | -
112 |
113 |
114 |
115 |
116 | -
117 |
118 |
119 | -
120 |
121 |
122 |
123 |
124 |
--------------------------------------------------------------------------------