net.openhft.chronicle.bytes.MappedUniqueTimeProvider Maven / Gradle / Ivy
/*
* Copyright (c) 2016-2022 chronicle.software
*
* https://chronicle.software
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package net.openhft.chronicle.bytes;
import net.openhft.chronicle.core.Jvm;
import net.openhft.chronicle.core.OS;
import net.openhft.chronicle.core.io.IORuntimeException;
import net.openhft.chronicle.core.io.Monitorable;
import net.openhft.chronicle.core.io.ReferenceOwner;
import net.openhft.chronicle.core.time.SystemTimeProvider;
import net.openhft.chronicle.core.time.TimeProvider;
import java.io.File;
/**
* Provides timestamps that are guaranteed to be unique across threads and processes on a single machine.
*
* This class utilizes a shared timestamp file, named with the user's name and located in a defined directory, to ensure the uniqueness of timestamps.
* By default, the file is located in the system's temporary directory.
*
* Each timestamp generated by an instance of this class is globally unique, making it ideal for synchronization and ordering across different threads and processes.
*/
public enum MappedUniqueTimeProvider implements TimeProvider, ReferenceOwner {
INSTANCE;
private static final int LAST_TIME = 128;
private static final int NANOS_PER_MICRO = 1000;
private final BytesStore, ?> bytesStore;
private TimeProvider provider = SystemTimeProvider.INSTANCE;
MappedUniqueTimeProvider() {
try {
String user = Jvm.getProperty("user.name", "unknown");
String timeStampDir = Jvm.getProperty("timestamp.dir", OS.TMP);
final File timeStampPath = new File(timeStampDir, ".time-stamp." + user + ".dat");
MappedFile file = MappedFile.ofSingle(timeStampPath, PageUtil.getPageSize(timeStampPath.getAbsolutePath()), false);
final Bytes> bytes = file.acquireBytesForWrite(this, 0);
bytes.append8bit("&TSF\nTime stamp file used for sharing a unique id\n");
this.bytesStore = bytes.bytesStore();
Monitorable.unmonitor(file);
Monitorable.unmonitor(bytes);
} catch (Exception ioe) {
throw new IORuntimeException(ioe);
}
}
// Todo: Handle thread safety
public MappedUniqueTimeProvider provider(TimeProvider provider) {
this.provider = provider;
return this;
}
/**
* @return Ordinary millisecond timestamp
*/
@Override
public long currentTimeMillis() {
return provider.currentTimeMillis();
}
@Override
public long currentTimeMicros()
throws IllegalStateException {
long timeus = provider.currentTimeMicros();
while (true) {
final long time0 = lastTimeStored();
long time0us = time0 / NANOS_PER_MICRO;
long time;
if (time0us >= timeus)
time = (time0us + 1) * NANOS_PER_MICRO;
else
time = timeus * NANOS_PER_MICRO;
if (casLastTimeStored(time0, time))
return time / NANOS_PER_MICRO;
Jvm.nanoPause();
}
}
@Override
public long currentTimeNanos()
throws IllegalStateException {
long time = provider.currentTimeNanos();
long time5 = time >>> 5;
long time0 = lastTimeStored();
long timeNanos5 = time0 >>> 5;
if (time5 > timeNanos5 && casLastTimeStored(time0, time))
return time;
while (true) {
time0 = lastTimeStored();
long next = (time0 + 0x20) & ~0x1f;
if (casLastTimeStored(time0, next))
return next;
Jvm.nanoPause();
}
}
private long lastTimeStored() {
return bytesStore.readVolatileLong(LAST_TIME);
}
private boolean casLastTimeStored(final long expected, final long value) {
return ((RandomDataOutput>) bytesStore).compareAndSwapLong(LAST_TIME, expected, value);
}
}