blob: cd33266150d1f357c46c0eaee246c932065ed07f [file] [log] [blame]
// Lucene version compatibility level 4.8.1
using RandomizedTesting.Generators;
using System;
using System.Diagnostics;
using System.Threading;
namespace Lucene.Net.Facet
{
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You 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.
*/
using IndexInput = Lucene.Net.Store.IndexInput;
using IndexOutput = Lucene.Net.Store.IndexOutput;
using IOContext = Lucene.Net.Store.IOContext;
using RAMDirectory = Lucene.Net.Store.RAMDirectory;
/// <summary>
/// Test utility - slow directory
/// </summary>
// TODO: move to test-framework and sometimes use in tests?
public class SlowRAMDirectory : RAMDirectory
{
private const int IO_SLEEP_THRESHOLD = 50;
internal Random random;
private int sleepMillis;
public virtual void SetSleepMillis(int sleepMillis)
{
this.sleepMillis = sleepMillis;
}
public SlowRAMDirectory(int sleepMillis, Random random)
{
this.sleepMillis = sleepMillis;
this.random = random;
}
public override IndexOutput CreateOutput(string name, IOContext context)
{
if (sleepMillis != -1)
{
return new SlowIndexOutput(this, base.CreateOutput(name, context));
}
return base.CreateOutput(name, context);
}
public override IndexInput OpenInput(string name, IOContext context)
{
if (sleepMillis != -1)
{
return new SlowIndexInput(this, base.OpenInput(name, context));
}
return base.OpenInput(name, context);
}
internal virtual void DoSleep(Random random, int length)
{
int sTime = length < 10 ? sleepMillis : (int)(sleepMillis * Math.Log(length));
if (random != null)
{
sTime = random.Next(sTime);
}
try
{
Sleep(sTime);
}
catch (Exception ie) when (ie.IsInterruptedException())
{
throw new Util.ThreadInterruptedException(ie);
}
}
// LUCENENET specific - We can't use Thread.Sleep which depends on the clock
// interrupt frequency, and that frequency might be too low for low values like 1 millisecond.
private static void Sleep(int milliseconds)
{
long ticks = (long)((Stopwatch.Frequency / (double)1000) * milliseconds); // ticks per millisecond * milliseconds = total delay ticks
long initialTick = Stopwatch.GetTimestamp();
long targetTick = initialTick + ticks;
while (Stopwatch.GetTimestamp() < targetTick)
{
Thread.SpinWait(1);
}
}
/// <summary>
/// Make a private random. </summary>
internal virtual Random ForkRandom()
{
if (random is null)
{
return null;
}
return new J2N.Randomizer(random.NextInt64());
}
/// <summary>
/// Delegate class to wrap an IndexInput and delay reading bytes by some
/// specified time.
/// </summary>
private class SlowIndexInput : IndexInput
{
private readonly SlowRAMDirectory outerInstance;
private readonly IndexInput ii;
private int numRead = 0;
private readonly Random rand;
public SlowIndexInput(SlowRAMDirectory outerInstance, IndexInput ii)
: base("SlowIndexInput(" + ii + ")")
{
this.outerInstance = outerInstance;
this.rand = outerInstance.ForkRandom();
this.ii = ii;
}
public override byte ReadByte()
{
if (numRead >= IO_SLEEP_THRESHOLD)
{
outerInstance.DoSleep(rand, 0);
numRead = 0;
}
++numRead;
return ii.ReadByte();
}
// LUCENENET: Use Span<byte> instead of byte[] for better compatibility.
public override void ReadBytes(Span<byte> destination)
{
int len = destination.Length;
if (numRead >= IO_SLEEP_THRESHOLD)
{
outerInstance.DoSleep(rand, len);
numRead = 0;
}
numRead += len;
ii.ReadBytes(destination);
}
// TODO: is it intentional that clone doesnt wrap?
public override object Clone()
{
return ii.Clone();
}
protected override void Dispose(bool disposing)
{
if (disposing)
{
ii.Dispose();
}
}
public override bool Equals(object o)
{
return ii.Equals(o);
}
public override long Position => ii.Position; // LUCENENET specific: Renamed from getFilePointer() to match FileStream
public override int GetHashCode()
{
return ii.GetHashCode();
}
public override long Length => ii.Length;
public override void Seek(long pos)
{
ii.Seek(pos);
}
}
/// <summary>
/// Delegate class to wrap an IndexOutput and delay writing bytes by some
/// specified time.
/// </summary>
private class SlowIndexOutput : IndexOutput
{
private readonly SlowRAMDirectory outerInstance;
private readonly IndexOutput io;
private int numWrote;
private readonly Random rand;
public SlowIndexOutput(SlowRAMDirectory outerInstance, IndexOutput io)
{
this.outerInstance = outerInstance;
this.io = io;
this.rand = outerInstance.ForkRandom();
}
public override void WriteByte(byte b)
{
if (numWrote >= IO_SLEEP_THRESHOLD)
{
outerInstance.DoSleep(rand, 0);
numWrote = 0;
}
++numWrote;
io.WriteByte(b);
}
// LUCENENET: Use ReadOnlySpan<byte> instead of byte[] for better compatibility.
public override void WriteBytes(ReadOnlySpan<byte> source)
{
int length = source.Length;
if (numWrote >= IO_SLEEP_THRESHOLD)
{
outerInstance.DoSleep(rand, length);
numWrote = 0;
}
numWrote += length;
io.WriteBytes(source);
}
[Obsolete]
public override void Seek(long pos)
{
io.Seek(pos);
}
protected override void Dispose(bool disposing)
{
if (disposing)
{
io.Dispose();
}
}
public override void Flush()
{
io.Flush();
}
public override long Position => io.Position; // LUCENENET specific: Renamed from getFilePointer() to match FileStream
public override long Length
{
get => io.Length;
set => throw IllegalStateException.Create("Length is readonly"); // LUCENENET specific: We cannot override get without also overriding set, so we throw if it is set
}
public override long Checksum => io.Checksum;
}
}
}