blob: 73f2e5d7397bb8444e3c9f4771a7351177f0c4e8 [file] [log] [blame]
using J2N.Threading;
using Lucene.Net.Documents;
using NUnit.Framework;
using System;
using System.IO;
using System.Threading;
using Assert = Lucene.Net.TestFramework.Assert;
using Console = Lucene.Net.Util.SystemConsole;
namespace Lucene.Net.Index
{
/*
* 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 BytesRef = Lucene.Net.Util.BytesRef;
using Directory = Lucene.Net.Store.Directory;
using Document = Documents.Document;
using Field = Field;
using LuceneTestCase = Lucene.Net.Util.LuceneTestCase;
using MockAnalyzer = Lucene.Net.Analysis.MockAnalyzer;
using TextField = TextField;
[TestFixture]
public class TestIndexWriterNRTIsCurrent : LuceneTestCase
{
public class ReaderHolder
{
internal volatile DirectoryReader reader;
internal volatile bool stop = false;
}
[Test]
public virtual void TestIsCurrentWithThreads()
{
Directory dir = NewDirectory();
IndexWriterConfig conf = NewIndexWriterConfig(TEST_VERSION_CURRENT, new MockAnalyzer(Random));
IndexWriter writer = new IndexWriter(dir, conf);
ReaderHolder holder = new ReaderHolder();
ReaderThread[] threads = new ReaderThread[AtLeast(3)];
CountdownEvent latch = new CountdownEvent(1);
WriterThread writerThread = new WriterThread(holder, writer, AtLeast(500), Random, latch);
for (int i = 0; i < threads.Length; i++)
{
threads[i] = new ReaderThread(holder, latch);
threads[i].Start();
}
writerThread.Start();
writerThread.Join();
bool failed = writerThread.failed != null;
if (failed)
{
Console.WriteLine(writerThread.failed.ToString());
Console.Write(writerThread.failed.StackTrace);
}
for (int i = 0; i < threads.Length; i++)
{
threads[i].Join();
if (threads[i].failed != null)
{
Console.WriteLine(threads[i].failed.ToString());
Console.Write(threads[i].failed.StackTrace);
failed = true;
}
}
Assert.IsFalse(failed);
writer.Dispose();
dir.Dispose();
}
public class WriterThread : ThreadJob
{
internal readonly ReaderHolder holder;
internal readonly IndexWriter writer;
internal readonly int numOps;
internal bool countdown = true;
internal readonly CountdownEvent latch;
internal Exception failed;
internal WriterThread(ReaderHolder holder, IndexWriter writer, int numOps, Random random, CountdownEvent latch)
: base()
{
this.holder = holder;
this.writer = writer;
this.numOps = numOps;
this.latch = latch;
}
public override void Run()
{
DirectoryReader currentReader = null;
Random random = LuceneTestCase.Random;
try
{
Document doc = new Document();
doc.Add(new TextField("id", "1", Field.Store.NO));
writer.AddDocument(doc);
holder.reader = currentReader = writer.GetReader(true);
Term term = new Term("id");
for (int i = 0; i < numOps && !holder.stop; i++)
{
float nextOp = (float)random.NextDouble();
if (nextOp < 0.3)
{
term.Set("id", new BytesRef("1"));
writer.UpdateDocument(term, doc);
}
else if (nextOp < 0.5)
{
writer.AddDocument(doc);
}
else
{
term.Set("id", new BytesRef("1"));
writer.DeleteDocuments(term);
}
if (holder.reader != currentReader)
{
holder.reader = currentReader;
if (countdown)
{
countdown = false;
latch.Signal();
}
}
if (random.NextBoolean())
{
writer.Commit();
DirectoryReader newReader = DirectoryReader.OpenIfChanged(currentReader);
if (newReader != null)
{
currentReader.DecRef();
currentReader = newReader;
}
if (currentReader.NumDocs == 0)
{
writer.AddDocument(doc);
}
}
}
}
catch (Exception e)
{
failed = e;
}
finally
{
holder.reader = null;
if (countdown)
{
latch.Signal();
}
if (currentReader != null)
{
try
{
currentReader.DecRef();
}
#pragma warning disable 168
catch (IOException e)
#pragma warning restore 168
{
}
}
}
if (Verbose)
{
Console.WriteLine("writer stopped - forced by reader: " + holder.stop);
}
}
}
public sealed class ReaderThread : ThreadJob
{
internal readonly ReaderHolder holder;
internal readonly CountdownEvent latch;
internal Exception failed;
internal ReaderThread(ReaderHolder holder, CountdownEvent latch)
: base()
{
this.holder = holder;
this.latch = latch;
}
public override void Run()
{
#if FEATURE_THREAD_INTERRUPT
try
{
#endif
latch.Wait();
#if FEATURE_THREAD_INTERRUPT
}
catch (ThreadInterruptedException e)
{
failed = e;
return;
}
#endif
DirectoryReader reader;
while ((reader = holder.reader) != null)
{
if (reader.TryIncRef())
{
try
{
bool current = reader.IsCurrent();
if (Verbose)
{
Console.WriteLine("Thread: " + Thread.CurrentThread + " Reader: " + reader + " isCurrent:" + current);
}
Assert.IsFalse(current);
}
catch (Exception e)
{
if (Verbose)
{
Console.WriteLine("FAILED Thread: " + Thread.CurrentThread + " Reader: " + reader + " isCurrent: false");
}
failed = e;
holder.stop = true;
return;
}
finally
{
try
{
reader.DecRef();
}
catch (IOException e)
{
if (failed == null)
{
failed = e;
}
}
}
return;
}
}
}
}
}
}