Make recorder and RecordingManager thread-safe
This commit is contained in:
parent
f2cae6a312
commit
da486bbf4a
|
@ -101,16 +101,31 @@ public class NextGenLocalRecorder implements Recorder {
|
|||
try {
|
||||
Future<Recording> result = completionService.take();
|
||||
Recording recording = result.get();
|
||||
recordingsLock.lock();
|
||||
try {
|
||||
recordingProcesses.remove(recording.getModel());
|
||||
} finally {
|
||||
recordingsLock.unlock();
|
||||
}
|
||||
if (recording.getStatus() == State.WAITING) {
|
||||
LOG.debug("Download finished for {} -> Starting post-processing", recording.getModel().getName());
|
||||
ppPool.submit(() -> {
|
||||
try {
|
||||
setRecordingStatus(recording, State.POST_PROCESSING);
|
||||
recordingManager.saveRecording(recording);
|
||||
recording.postprocess();
|
||||
setRecordingStatus(recording, State.FINISHED);
|
||||
recordingManager.saveRecording(recording);
|
||||
return recording;
|
||||
deleteIfTooShort(recording);
|
||||
} catch (Exception e) {
|
||||
LOG.error("Error while post-processing recording {}", recording, e);
|
||||
recording.setStatus(State.FAILED);
|
||||
try {
|
||||
recordingManager.saveRecording(recording);
|
||||
} catch (IOException e1) {
|
||||
LOG.error("Couldn't update recording state for recording {}", recording, e1);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
// check, if we have to restart the recording
|
||||
|
@ -118,19 +133,12 @@ public class NextGenLocalRecorder implements Recorder {
|
|||
tryRestartRecording(model);
|
||||
} else {
|
||||
if(recording.getStatus() != State.DELETED) {
|
||||
recordingsLock.lock();
|
||||
try {
|
||||
recordingManager.delete(recording);
|
||||
} catch (IOException e) {
|
||||
LOG.error("Couldn't delete recording {}", recording, e);
|
||||
} finally {
|
||||
recordingsLock.unlock();
|
||||
}
|
||||
delete(recording);
|
||||
}
|
||||
setRecordingStatus(recording, State.FAILED);
|
||||
}
|
||||
} catch (InterruptedException | ExecutionException e) {
|
||||
e.printStackTrace();
|
||||
} catch (InterruptedException | ExecutionException | InvalidKeyException | NoSuchAlgorithmException | IllegalStateException | IOException e) {
|
||||
LOG.error("Error while completing recording", e);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
@ -231,7 +239,7 @@ public class NextGenLocalRecorder implements Recorder {
|
|||
|
||||
Recording rec = new Recording();
|
||||
rec.setDownload(download);
|
||||
rec.setPath(download.getPath(model));
|
||||
rec.setPath(download.getPath(model).replaceAll("\\\\", "/"));
|
||||
rec.setModel(model);
|
||||
rec.setStartDate(Instant.ofEpochMilli(System.currentTimeMillis()));
|
||||
recordingProcesses.put(model, rec);
|
||||
|
@ -241,7 +249,7 @@ public class NextGenLocalRecorder implements Recorder {
|
|||
setRecordingStatus(rec, State.RECORDING);
|
||||
recordingManager.saveRecording(rec);
|
||||
download.start();
|
||||
boolean deleted = deleteIfTooShort(rec);
|
||||
boolean deleted = deleteIfEmpty(rec);
|
||||
setRecordingStatus(rec, deleted ? State.DELETED : State.WAITING);
|
||||
recordingManager.saveRecording(rec);
|
||||
} catch (IOException e) {
|
||||
|
@ -254,15 +262,19 @@ public class NextGenLocalRecorder implements Recorder {
|
|||
}
|
||||
}
|
||||
|
||||
private boolean deleteIfTooShort(Recording rec) throws IOException, ParseException, PlaylistException {
|
||||
// if the size is 0, we don't need to go ahead and check the length
|
||||
private boolean deleteIfEmpty(Recording rec) throws IOException, ParseException, PlaylistException, InvalidKeyException, NoSuchAlgorithmException, IllegalStateException {
|
||||
rec.refresh();
|
||||
long sizeInByte = rec.getSizeInByte();
|
||||
if (sizeInByte == 0) {
|
||||
recordingManager.delete(rec);
|
||||
LOG.info("Deleting empty recording {}", rec);
|
||||
delete(rec);
|
||||
return true;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
private boolean deleteIfTooShort(Recording rec) throws IOException, ParseException, PlaylistException, InvalidKeyException, NoSuchAlgorithmException, IllegalStateException {
|
||||
Duration minimumLengthInSeconds = Duration.ofSeconds(Config.getInstance().getSettings().minimumLengthInSeconds);
|
||||
if (minimumLengthInSeconds.getSeconds() <= 0) {
|
||||
return false;
|
||||
|
@ -270,7 +282,8 @@ public class NextGenLocalRecorder implements Recorder {
|
|||
|
||||
Duration recordingLength = rec.getLength();
|
||||
if (recordingLength.compareTo(minimumLengthInSeconds) < 0) {
|
||||
recordingManager.delete(rec);
|
||||
LOG.info("Deleting too short recording {} [{} < {}]", rec, recordingLength, minimumLengthInSeconds);
|
||||
delete(rec);
|
||||
return true;
|
||||
}
|
||||
|
||||
|
@ -293,10 +306,15 @@ public class NextGenLocalRecorder implements Recorder {
|
|||
modelLock.unlock();
|
||||
}
|
||||
|
||||
recordingsLock.lock();
|
||||
try {
|
||||
if (recordingProcesses.containsKey(model)) {
|
||||
Recording recording = recordingProcesses.get(model);
|
||||
recording.getDownload().stop();
|
||||
}
|
||||
} finally {
|
||||
recordingsLock.unlock();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -306,10 +324,15 @@ public class NextGenLocalRecorder implements Recorder {
|
|||
models.get(index).setStreamUrlIndex(model.getStreamUrlIndex());
|
||||
config.save();
|
||||
LOG.debug("Switching stream source to index {} for model {}", model.getStreamUrlIndex(), model.getName());
|
||||
recordingsLock.lock();
|
||||
try {
|
||||
Recording recording = recordingProcesses.get(model);
|
||||
if (recording != null) {
|
||||
stopRecordingProcess(model);
|
||||
}
|
||||
} finally {
|
||||
recordingsLock.unlock();
|
||||
}
|
||||
tryRestartRecording(model);
|
||||
} else {
|
||||
LOG.warn("Couldn't switch stream source for model {}. Not found in list", model.getName());
|
||||
|
@ -318,10 +341,15 @@ public class NextGenLocalRecorder implements Recorder {
|
|||
}
|
||||
|
||||
private void stopRecordingProcess(Model model) {
|
||||
recordingsLock.lock();
|
||||
try {
|
||||
LOG.debug("Stopping recording for {}", model);
|
||||
Recording recording = recordingProcesses.get(model);
|
||||
LOG.debug("Stopping download for {}", model);
|
||||
recording.getDownload().stop();
|
||||
} finally {
|
||||
recordingsLock.unlock();
|
||||
}
|
||||
}
|
||||
|
||||
private void stopRecordingProcesses() {
|
||||
|
@ -357,12 +385,7 @@ public class NextGenLocalRecorder implements Recorder {
|
|||
|
||||
@Override
|
||||
public List<Recording> getRecordings() throws IOException, InvalidKeyException, NoSuchAlgorithmException, IllegalStateException {
|
||||
recordingsLock.lock();
|
||||
try {
|
||||
return recordingManager.getAll();
|
||||
} finally {
|
||||
recordingsLock.unlock();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -377,6 +400,8 @@ public class NextGenLocalRecorder implements Recorder {
|
|||
recording = false;
|
||||
|
||||
LOG.debug("Stopping all recording processes");
|
||||
recordingsLock.lock();
|
||||
try {
|
||||
// make a copy to avoid ConcurrentModificationException
|
||||
List<Recording> toStop = new ArrayList<>(recordingProcesses.values());
|
||||
for (Recording rec : toStop) {
|
||||
|
@ -392,6 +417,9 @@ public class NextGenLocalRecorder implements Recorder {
|
|||
LOG.error("Error while waiting for downloads to finish", e);
|
||||
}
|
||||
}
|
||||
} finally {
|
||||
recordingsLock.unlock();
|
||||
}
|
||||
|
||||
// shutdown threadpools
|
||||
try {
|
||||
|
@ -426,8 +454,13 @@ public class NextGenLocalRecorder implements Recorder {
|
|||
modelLock.unlock();
|
||||
}
|
||||
|
||||
recordingsLock.lock();
|
||||
try {
|
||||
Recording recording = recordingProcesses.get(model);
|
||||
Optional.ofNullable(recording).map(Recording::getDownload).ifPresent(Download::stop);
|
||||
} finally {
|
||||
recordingsLock.unlock();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -541,6 +574,7 @@ public class NextGenLocalRecorder implements Recorder {
|
|||
EventBusHolder.BUS.register(new Object() {
|
||||
@Subscribe
|
||||
public void modelEvent(Event e) {
|
||||
recordingsLock.lock();
|
||||
try {
|
||||
if (e.getType() == MODEL_ONLINE) {
|
||||
ModelIsOnlineEvent evt = (ModelIsOnlineEvent) e;
|
||||
|
@ -551,6 +585,8 @@ public class NextGenLocalRecorder implements Recorder {
|
|||
}
|
||||
} catch (Exception e1) {
|
||||
LOG.error("Error while handling model state changed event {}", e, e1);
|
||||
} finally {
|
||||
recordingsLock.unlock();
|
||||
}
|
||||
}
|
||||
});
|
||||
|
|
|
@ -9,6 +9,7 @@ import java.nio.file.Files;
|
|||
import java.time.Instant;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.concurrent.locks.ReentrantLock;
|
||||
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
@ -31,7 +32,7 @@ public class RecordingManager {
|
|||
private Moshi moshi;
|
||||
private JsonAdapter<Recording> adapter;
|
||||
private List<Recording> recordings = new ArrayList<>();
|
||||
// private RecordingFileMonitor monitor = new RecordingFileMonitor(this);
|
||||
private ReentrantLock recordingsLock = new ReentrantLock();
|
||||
|
||||
public RecordingManager(Config config, List<Site> sites) throws IOException {
|
||||
this.config = config;
|
||||
|
@ -42,13 +43,16 @@ public class RecordingManager {
|
|||
adapter = moshi.adapter(Recording.class).indent(" ");
|
||||
|
||||
loadRecordings();
|
||||
// startMonitoring();
|
||||
}
|
||||
|
||||
public void add(Recording rec) throws UnsupportedEncodingException, IOException {
|
||||
saveRecording(rec);
|
||||
recordingsLock.lock();
|
||||
try {
|
||||
recordings.add(rec);
|
||||
// registerFileWatch(rec);
|
||||
} finally {
|
||||
recordingsLock.unlock();
|
||||
}
|
||||
}
|
||||
|
||||
public void saveRecording(Recording rec) throws UnsupportedEncodingException, IOException {
|
||||
|
@ -81,35 +85,6 @@ public class RecordingManager {
|
|||
}
|
||||
}
|
||||
|
||||
// private void startMonitoring() {
|
||||
// for (Recording recording : recordings) {
|
||||
// registerFileWatch(recording);
|
||||
// }
|
||||
// Thread watcher = new Thread(() -> monitor.processEvents());
|
||||
// watcher.setDaemon(true);
|
||||
// watcher.setPriority(Thread.MIN_PRIORITY);
|
||||
// watcher.setName("RecordingFileMonitor");
|
||||
// watcher.start();
|
||||
// }
|
||||
//
|
||||
// private void registerFileWatch(Recording recording) {
|
||||
// File rec = recording.getAbsoluteFile();
|
||||
// if (rec.isDirectory()) {
|
||||
// monitor.register(rec.toPath());
|
||||
// } else {
|
||||
// monitor.register(rec.getParentFile().toPath());
|
||||
// }
|
||||
// }
|
||||
//
|
||||
// private void removeFileWatch(Recording recording) {
|
||||
// File rec = recording.getAbsoluteFile();
|
||||
// if (rec.isDirectory()) {
|
||||
// monitor.unregister(rec.toPath());
|
||||
// } else {
|
||||
// monitor.unregister(rec.getParentFile().toPath());
|
||||
// }
|
||||
// }
|
||||
|
||||
private boolean recordingExists(Recording recording) {
|
||||
File rec = new File(config.getSettings().recordingsDir, recording.getPath());
|
||||
return rec.exists();
|
||||
|
@ -122,6 +97,8 @@ public class RecordingManager {
|
|||
}
|
||||
|
||||
public void delete(Recording recording) throws IOException {
|
||||
recordingsLock.lock();
|
||||
try {
|
||||
int idx = recordings.indexOf(recording);
|
||||
recording = recordings.get(idx);
|
||||
|
||||
|
@ -145,15 +122,21 @@ public class RecordingManager {
|
|||
// remove from data structure
|
||||
recordings.remove(recording);
|
||||
recording.setStatus(State.DELETED);
|
||||
|
||||
// removeFileWatch(recording);
|
||||
} finally {
|
||||
recordingsLock.unlock();
|
||||
}
|
||||
}
|
||||
|
||||
public List<Recording> getAll() {
|
||||
recordingsLock.lock();
|
||||
try {
|
||||
for (Recording recording : recordings) {
|
||||
recording.refresh();
|
||||
}
|
||||
return new ArrayList<>(recordings);
|
||||
} finally {
|
||||
recordingsLock.unlock();
|
||||
}
|
||||
}
|
||||
|
||||
private void deleteEmptyParents(File parent) throws IOException {
|
||||
|
|
Loading…
Reference in New Issue