[svn r136] MAJOR UNSTABLE UPDATE!!!

Initial commit after moving to Tango instead of Phobos.

Lots of bugfixes...

This build is not suitable for most things.
This commit is contained in:
Tomas Lindquist Olsen
2008-01-11 17:57:40 +01:00
parent bc08c6fcb1
commit b15b3484c8
524 changed files with 270705 additions and 175 deletions

View File

@@ -0,0 +1,60 @@
/*******************************************************************************
*******************************************************************************/
public import tango.net.cluster.NetworkCall;
/*******************************************************************************
a Task function
*******************************************************************************/
real add (real x, real y)
{
return x + y;
}
/*******************************************************************************
a Task function
*******************************************************************************/
int divide (int x, int y)
{
return x / y;
}
/*******************************************************************************
a verbose Task message
*******************************************************************************/
class Subtract : NetworkCall
{
double a,
b,
result;
double opCall (double a, double b, IChannel channel = null)
{
this.a = a;
this.b = b;
send (channel);
return result;
}
override void execute ()
{
result = a - b;
}
override void read (IReader input) {input (a)(b)(result);}
override void write (IWriter output) {output (a)(b)(result);}
}

View File

@@ -0,0 +1,36 @@
private import tango.core.Thread;
private import tango.util.log.Configurator;
private import tango.net.cluster.NetworkAlert;
private import tango.net.cluster.tina.Cluster;
/*******************************************************************************
How to send and recieve Alert messages using tango.net.cluster
*******************************************************************************/
void main()
{
// hook into the cluster
auto cluster = (new Cluster).join;
// hook into the Alert layer
auto alert = new NetworkAlert (cluster, "my.kind.of.alert");
// listen for the broadcast (on this channel)
alert.createConsumer (delegate void (IEvent event)
{event.log.info ("Recieved alert on channel " ~ event.channel.name);}
);
// say what's going on
alert.log.info ("broadcasting alert");
// and send everyone an empty alert (on this channel)
alert.broadcast;
// wait for it to arrive ...
Thread.sleep(1);
}

View File

@@ -0,0 +1,48 @@
/*******************************************************************************
*******************************************************************************/
import tango.io.Stdout;
import tango.time.StopWatch;
import tango.util.log.Configurator;
import tango.net.cluster.NetworkCache;
import tango.net.cluster.tina.Cluster;
/*******************************************************************************
*******************************************************************************/
void main (char[][] args)
{
StopWatch w;
if (args.length > 1)
{
auto cluster = (new Cluster).join (args[1..$]);
auto cache = new NetworkCache (cluster, "my.cache.channel");
while (true)
{
w.start;
for (int i=10000; i--;)
cache.put ("key", cache.EmptyMessage);
Stdout.formatln ("{} put/s", 10000/w.stop);
w.start;
for (int i=10000; i--;)
cache.get ("key");
Stdout.formatln ("{} get/s", 10000/w.stop);
}
}
else
Stdout.formatln ("usage: cache cachehost:port ...");
}

View File

@@ -0,0 +1,30 @@
/*******************************************************************************
*******************************************************************************/
import tango.io.Console;
import tango.net.InternetAddress;
import tango.net.cluster.tina.CmdParser,
tango.net.cluster.tina.CacheServer;
/*******************************************************************************
*******************************************************************************/
void main (char[][] args)
{
auto arg = new CmdParser ("cache.server");
// default number of cache entries
arg.size = 8192;
if (args.length > 1)
arg.parse (args[1..$]);
if (arg.help)
Cout ("usage: cacheserver -port=number -size=cachesize -log[=trace, info, warn, error, fatal, none]").newline;
else
(new CacheServer(new InternetAddress(arg.port), arg.log, arg.size)).start;
}

View File

@@ -0,0 +1,38 @@
private import tango.core.Thread;
private import tango.util.log.Configurator;
private import tango.net.cluster.tina.Cluster;
private import tango.net.cluster.QueuedCache,
tango.net.cluster.CacheInvalidatee,
tango.net.cluster.CacheInvalidator;
/*******************************************************************************
Demonstrates how to invalidate cache entries across a cluster
via a channel
*******************************************************************************/
void main()
{
// access the cluster
auto cluster = (new Cluster).join;
// wrap a cache instance with a network listener
auto dst = new CacheInvalidatee (cluster, "my.cache.channel", new QueuedCache!(char[], IMessage)(101));
// connect an invalidator to that cache channel
auto src = new CacheInvalidator (cluster, "my.cache.channel");
// stuff something in the local cache
dst.cache.put ("key", dst.EmptyMessage);
// get it removed via a network broadcast
src.log.info ("invalidating 'key' across the cluster");
src.invalidate ("key");
// wait for it to arrive ...
Thread.sleep (1);
}

View File

@@ -0,0 +1,44 @@
/*******************************************************************************
*******************************************************************************/
import tango.io.Stdout;
import tango.time.StopWatch;
import tango.util.log.Configurator;
import tango.net.cluster.NetworkQueue;
import tango.net.cluster.tina.Cluster;
/*******************************************************************************
*******************************************************************************/
void main (char[][] args)
{
StopWatch w;
auto cluster = (new Cluster).join;
auto queue = new NetworkQueue (cluster, "my.queue.channel");
while (true)
{
w.start;
for (int i=10000; i--;)
queue.put (queue.EmptyMessage);
Stdout.formatln ("{} put/s", 10000/w.stop);
uint count;
w.start;
while (queue.get !is null)
++count;
Stdout.formatln ("{} get/s", count/w.stop);
}
}

View File

@@ -0,0 +1,41 @@
private import tango.core.Thread;
private import tango.util.log.Configurator;
private import tango.net.cluster.NetworkQueue;
private import tango.net.cluster.tina.Cluster;
/*******************************************************************************
Illustrates how to setup and use a Queue in asynchronous mode
*******************************************************************************/
void main ()
{
void listen (IEvent event)
{
while (event.get)
event.log.info ("received asynch msg on channel " ~ event.channel.name);
}
// join the cluster
auto cluster = (new Cluster).join;
// access a queue of the specified name
auto queue = new NetworkQueue (cluster, "my.queue.channel");
// listen for messages placed in my queue, via a delegate
queue.createConsumer (&listen);
// stuff something into the queue
queue.log.info ("sending three messages to the queue");
queue.put (queue.EmptyMessage);
queue.put (queue.EmptyMessage);
queue.put (queue.EmptyMessage);
// wait for asynchronous msgs to arrive ...
Thread.sleep (1);
}

View File

@@ -0,0 +1,30 @@
private import tango.util.log.Configurator;
private import tango.net.cluster.NetworkQueue;
private import tango.net.cluster.tina.Cluster;
/*******************************************************************************
Illustrates how to setup and use a Queue in synchronous mode
*******************************************************************************/
void main ()
{
// join the cluster
auto cluster = (new Cluster).join;
// access a queue of the specified name
auto queue = new NetworkQueue (cluster, "my.queue.channel");
// stuff something into the queue
queue.log.info ("sending three messages to the queue");
queue.put (queue.EmptyMessage);
queue.put (queue.EmptyMessage);
queue.put (queue.EmptyMessage);
// retreive synchronously
while (queue.get)
queue.log.info ("retrieved msg");
}

View File

@@ -0,0 +1,27 @@
/*******************************************************************************
*******************************************************************************/
import tango.io.Console;
import tango.net.InternetAddress;
import tango.net.cluster.tina.CmdParser,
tango.net.cluster.tina.QueueServer;
/*******************************************************************************
*******************************************************************************/
void main (char[][] args)
{
auto arg = new CmdParser ("queue.server");
if (args.length > 1)
arg.parse (args[1..$]);
if (arg.help)
Cout ("usage: queueserver -port=number -log[=trace, info, warn, error, fatal, none]").newline;
else
(new QueueServer(new InternetAddress(arg.port), arg.log)).start;
}

View File

@@ -0,0 +1,38 @@
private import tango.core.Thread;
private import tango.util.log.Configurator;
private import tango.net.cluster.tina.Cluster;
private import tango.net.cluster.NetworkQueue;
/*******************************************************************************
*******************************************************************************/
void main()
{
// open the cluster and a queue channel. Note that the queue has
// been configured with a reply listener ...
auto cluster = (new Cluster).join;
auto queue = new NetworkQueue (cluster, "message.channel",
(IEvent event){event.log.info ("Received reply");}
);
void recipient (IEvent event)
{
auto msg = event.get;
event.log.info ("Replying to message on channel "~msg.reply);
event.reply (event.replyChannel(msg), queue.EmptyMessage);
}
// setup a listener to recieve and reply
queue.createConsumer (&recipient);
// toss a message out to the cluster
queue.put (queue.EmptyMessage);
// wait for completion ...
Thread.sleep (1);
}

View File

@@ -0,0 +1,15 @@
/*******************************************************************************
Illustrates usage of cluster tasks
*******************************************************************************/
import Add, tango.io.Stdout, tango.net.cluster.tina.ClusterTask;
void main (char[][] args)
{
scope add = new NetCall!(add);
Stdout.formatln ("cluster expression of 3.0 + 4.0 = {}", add(3, 4));
}

View File

@@ -0,0 +1,42 @@
/*******************************************************************************
*******************************************************************************/
import Add;
import tango.io.Stdout;
import tango.time.StopWatch;
import tango.util.log.Configurator;
import tango.net.cluster.tina.ClusterTask;
/*******************************************************************************
*******************************************************************************/
void main (char[][] args)
{
// an implicit task instance
auto add = new NetCall!(add);
// an explicit task instance
auto sub = new Subtract;
StopWatch w;
while (true)
{
w.start;
for (int i=10000; i--;)
{
// both task types are used in the same manner
add (1, 2);
sub (3, 4);
}
Stdout.formatln ("{} calls/s", 20000/w.stop);
}
}

View File

@@ -0,0 +1,34 @@
/*******************************************************************************
*******************************************************************************/
import tango.io.Console;
import tango.net.InternetAddress;
import tango.net.cluster.tina.CmdParser,
tango.net.cluster.tina.TaskServer;
import Add;
/*******************************************************************************
*******************************************************************************/
void main (char[][] args)
{
auto arg = new CmdParser ("task.server");
if (args.length > 1)
arg.parse (args[1..$]);
if (arg.help)
Cout ("usage: taskserver -port=number -log[=trace, info, warn, error, fatal, none]").newline;
else
{
auto server = new TaskServer (new InternetAddress(arg.port), arg.log);
server.enroll (new NetCall!(add));
server.enroll (new Subtract);
server.start;
}
}

View File

@@ -0,0 +1,806 @@
import tango.core.Thread;
extern (C) int printf(char * str, ...);
void main()
{
printf("Compile with -unittest");
}
unittest
{
printf("Testing context creation/deletion\n");
int s0 = 0;
static int s1 = 0;
Fiber a = new Fiber(
delegate void()
{
s0++;
});
static void fb() { s1++; }
Fiber b = new Fiber(&fb);
Fiber c = new Fiber(
delegate void() { assert(false); });
assert(a);
assert(b);
assert(c);
assert(s0 == 0);
assert(s1 == 0);
assert(a.state == Fiber.State.HOLD);
assert(b.state == Fiber.State.HOLD);
assert(c.state == Fiber.State.HOLD);
delete c;
assert(s0 == 0);
assert(s1 == 0);
assert(a.state == Fiber.State.HOLD);
assert(b.state == Fiber.State.HOLD);
printf("running a\n");
a.call();
printf("done a\n");
assert(a);
assert(s0 == 1);
assert(s1 == 0);
assert(a.state == Fiber.State.TERM);
assert(b.state == Fiber.State.HOLD);
assert(b.state == Fiber.State.HOLD);
printf("Running b\n");
b.call();
printf("Done b\n");
assert(s0 == 1);
assert(s1 == 1);
assert(b.state == Fiber.State.TERM);
delete a;
delete b;
printf("Context creation passed\n");
}
unittest
{
printf("Testing context switching\n");
int s0 = 0;
int s1 = 0;
int s2 = 0;
Fiber a = new Fiber(
delegate void()
{
while(true)
{
debug printf(" ---A---\n");
s0++;
Fiber.yield();
}
});
Fiber b = new Fiber(
delegate void()
{
while(true)
{
debug printf(" ---B---\n");
s1++;
Fiber.yield();
}
});
Fiber c = new Fiber(
delegate void()
{
while(true)
{
debug printf(" ---C---\n");
s2++;
Fiber.yield();
}
});
assert(a);
assert(b);
assert(c);
assert(s0 == 0);
assert(s1 == 0);
assert(s2 == 0);
a.call();
b.call();
assert(a);
assert(b);
assert(c);
assert(s0 == 1);
assert(s1 == 1);
assert(s2 == 0);
for(int i=0; i<20; i++)
{
c.call();
a.call();
}
assert(a);
assert(b);
assert(c);
assert(s0 == 21);
assert(s1 == 1);
assert(s2 == 20);
delete a;
delete b;
delete c;
printf("Context switching passed\n");
}
unittest
{
printf("Testing nested contexts\n");
Fiber a, b, c;
int t0 = 0;
int t1 = 0;
int t2 = 0;
a = new Fiber(
delegate void()
{
t0++;
b.call();
});
b = new Fiber(
delegate void()
{
assert(t0 == 1);
assert(t1 == 0);
assert(t2 == 0);
t1++;
c.call();
});
c = new Fiber(
delegate void()
{
assert(t0 == 1);
assert(t1 == 1);
assert(t2 == 0);
t2++;
});
assert(a);
assert(b);
assert(c);
assert(t0 == 0);
assert(t1 == 0);
assert(t2 == 0);
a.call();
assert(t0 == 1);
assert(t1 == 1);
assert(t2 == 1);
assert(a);
assert(b);
assert(c);
delete a;
delete b;
delete c;
printf("Nesting contexts passed\n");
}
unittest
{
printf("Testing basic exceptions\n");
int t0 = 0;
int t1 = 0;
int t2 = 0;
assert(t0 == 0);
assert(t1 == 0);
assert(t2 == 0);
try
{
try
{
throw new Exception("Testing\n");
t2++;
}
catch(Exception fx)
{
t1++;
throw fx;
}
t2++;
}
catch(Exception ex)
{
t0++;
printf("%.*s\n", ex.toString);
}
assert(t0 == 1);
assert(t1 == 1);
assert(t2 == 0);
printf("Basic exceptions are supported\n");
}
unittest
{
printf("Testing exceptions\n");
Fiber a, b, c;
int t0 = 0;
int t1 = 0;
int t2 = 0;
printf("t0 = %d\nt1 = %d\nt2 = %d\n", t0, t1, t2);
a = new Fiber(
delegate void()
{
t0++;
throw new Exception("A exception\n");
t0++;
});
b = new Fiber(
delegate void()
{
t1++;
c.call();
t1++;
});
c = new Fiber(
delegate void()
{
t2++;
throw new Exception("C exception\n");
t2++;
});
assert(a);
assert(b);
assert(c);
assert(t0 == 0);
assert(t1 == 0);
assert(t2 == 0);
try
{
a.call();
assert(false);
}
catch(Exception e)
{
printf("%.*s\n", e.toString);
}
assert(a);
assert(a.state == Fiber.State.TERM);
assert(b);
assert(c);
assert(t0 == 1);
assert(t1 == 0);
assert(t2 == 0);
try
{
b.call();
assert(false);
}
catch(Exception e)
{
printf("%.*s\n", e.toString);
}
printf("blah2\n");
assert(a);
assert(b);
assert(b.state == Fiber.State.TERM);
assert(c);
assert(c.state == Fiber.State.TERM);
assert(t0 == 1);
assert(t1 == 1);
assert(t2 == 1);
delete a;
delete b;
delete c;
Fiber t;
int q0 = 0;
int q1 = 0;
t = new Fiber(
delegate void()
{
try
{
q0++;
throw new Exception("T exception\n");
q0++;
}
catch(Exception ex)
{
q1++;
printf("!!!!!!!!GOT EXCEPTION!!!!!!!!\n");
printf("%.*s\n", ex.toString);
}
});
assert(t);
assert(q0 == 0);
assert(q1 == 0);
t.call();
assert(t);
assert(t.state == Fiber.State.TERM);
assert(q0 == 1);
assert(q1 == 1);
delete t;
Fiber d, e;
int s0 = 0;
int s1 = 0;
d = new Fiber(
delegate void()
{
try
{
s0++;
e.call();
Fiber.yield();
s0++;
e.call();
s0++;
}
catch(Exception ex)
{
printf("%.*s\n", ex.toString);
}
});
e = new Fiber(
delegate void()
{
s1++;
Fiber.yield();
throw new Exception("E exception\n");
s1++;
});
assert(d);
assert(e);
assert(s0 == 0);
assert(s1 == 0);
d.call();
assert(d);
assert(e);
assert(s0 == 1);
assert(s1 == 1);
d.call();
assert(d);
assert(e);
assert(s0 == 2);
assert(s1 == 1);
assert(d.state == Fiber.State.TERM);
assert(e.state == Fiber.State.TERM);
delete d;
delete e;
printf("Exceptions passed\n");
}
unittest
{
printf("Testing standard exceptions\n");
int t = 0;
Fiber a = new Fiber(
delegate void()
{
throw new Exception("BLAHAHA");
});
assert(a);
assert(t == 0);
try
{
a.call();
assert(false);
}
catch(Exception e)
{
printf("%.*s\n", e.toString);
}
assert(a);
assert(a.state == Fiber.State.TERM);
assert(t == 0);
delete a;
printf("Standard exceptions passed\n");
}
unittest
{
printf("Memory stress test\n");
const uint STRESS_SIZE = 5000;
Fiber ctx[];
ctx.length = STRESS_SIZE;
int cnt0 = 0;
int cnt1 = 0;
void threadFunc()
{
cnt0++;
Fiber.yield;
cnt1++;
}
foreach(inout Fiber c; ctx)
{
c = new Fiber(&threadFunc, 1024);
}
assert(cnt0 == 0);
assert(cnt1 == 0);
foreach(inout Fiber c; ctx)
{
c.call;
}
assert(cnt0 == STRESS_SIZE);
assert(cnt1 == 0);
foreach(inout Fiber c; ctx)
{
c.call;
}
assert(cnt0 == STRESS_SIZE);
assert(cnt1 == STRESS_SIZE);
foreach(inout Fiber c; ctx)
{
delete c;
}
assert(cnt0 == STRESS_SIZE);
assert(cnt1 == STRESS_SIZE);
printf("Memory stress test passed\n");
}
unittest
{
printf("Testing floating point\n");
float f0 = 1.0;
float f1 = 0.0;
double d0 = 2.0;
double d1 = 0.0;
real r0 = 3.0;
real r1 = 0.0;
assert(f0 == 1.0);
assert(f1 == 0.0);
assert(d0 == 2.0);
assert(d1 == 0.0);
assert(r0 == 3.0);
assert(r1 == 0.0);
Fiber a, b, c;
a = new Fiber(
delegate void()
{
while(true)
{
f0 ++;
d0 ++;
r0 ++;
Fiber.yield();
}
});
b = new Fiber(
delegate void()
{
while(true)
{
f1 = d0 + r0;
d1 = f0 + r0;
r1 = f0 + d0;
Fiber.yield();
}
});
c = new Fiber(
delegate void()
{
while(true)
{
f0 *= d1;
d0 *= r1;
r0 *= f1;
Fiber.yield();
}
});
a.call();
assert(f0 == 2.0);
assert(f1 == 0.0);
assert(d0 == 3.0);
assert(d1 == 0.0);
assert(r0 == 4.0);
assert(r1 == 0.0);
b.call();
assert(f0 == 2.0);
assert(f1 == 7.0);
assert(d0 == 3.0);
assert(d1 == 6.0);
assert(r0 == 4.0);
assert(r1 == 5.0);
c.call();
assert(f0 == 12.0);
assert(f1 == 7.0);
assert(d0 == 15.0);
assert(d1 == 6.0);
assert(r0 == 28.0);
assert(r1 == 5.0);
a.call();
assert(f0 == 13.0);
assert(f1 == 7.0);
assert(d0 == 16.0);
assert(d1 == 6.0);
assert(r0 == 29.0);
assert(r1 == 5.0);
printf("Floating point passed\n");
}
version(x86) unittest
{
printf("Testing registers\n");
struct registers
{
int eax, ebx, ecx, edx;
int esi, edi;
int ebp, esp;
//TODO: Add fpu stuff
}
static registers old;
static registers next;
static registers g_old;
static registers g_next;
//I believe that D calling convention requires that
//EBX, ESI and EDI be saved. In order to validate
//this, we write to those registers and call the
//stack thread.
static StackThread reg_test = new StackThread(
delegate void()
{
asm
{
naked;
pushad;
mov EBX, 1;
mov ESI, 2;
mov EDI, 3;
mov [old.ebx], EBX;
mov [old.esi], ESI;
mov [old.edi], EDI;
mov [old.ebp], EBP;
mov [old.esp], ESP;
call StackThread.yield;
mov [next.ebx], EBX;
mov [next.esi], ESI;
mov [next.edi], EDI;
mov [next.ebp], EBP;
mov [next.esp], ESP;
popad;
}
});
//Run the stack context
asm
{
naked;
pushad;
mov EBX, 10;
mov ESI, 11;
mov EDI, 12;
mov [g_old.ebx], EBX;
mov [g_old.esi], ESI;
mov [g_old.edi], EDI;
mov [g_old.ebp], EBP;
mov [g_old.esp], ESP;
mov EAX, [reg_test];
call StackThread.call;
mov [g_next.ebx], EBX;
mov [g_next.esi], ESI;
mov [g_next.edi], EDI;
mov [g_next.ebp], EBP;
mov [g_next.esp], ESP;
popad;
}
//Make sure the registers are byte for byte equal.
assert(old.ebx = 1);
assert(old.esi = 2);
assert(old.edi = 3);
assert(old == next);
assert(g_old.ebx = 10);
assert(g_old.esi = 11);
assert(g_old.edi = 12);
assert(g_old == g_next);
printf("Registers passed!\n");
}
unittest
{
printf("Testing throwYield\n");
int q0 = 0;
Fiber st0 = new Fiber(
delegate void()
{
q0++;
Fiber.yieldAndThrow(new Exception("testing throw yield\n"));
q0++;
});
try
{
st0.call();
assert(false);
}
catch(Exception e)
{
printf("%.*s\n", e.toString);
}
assert(q0 == 1);
assert(st0.state == Fiber.State.HOLD);
st0.call();
assert(q0 == 2);
assert(st0.state == Fiber.State.TERM);
printf("throwYield passed!\n");
}
unittest
{
printf("Testing thread safety\n");
int x = 0, y = 0;
Fiber sc0 = new Fiber(
{
while(true)
{
x++;
Fiber.yield;
}
});
Fiber sc1 = new Fiber(
{
while(true)
{
y++;
Fiber.yield;
}
});
Thread t0 = new Thread(
{
for(int i=0; i<10000; i++)
sc0.call();
});
Thread t1 = new Thread(
{
for(int i=0; i<10000; i++)
sc1.call();
});
assert(sc0);
assert(sc1);
assert(t0);
assert(t1);
t0.start;
t1.start;
t0.join;
t1.join;
assert(x == 10000);
assert(y == 10000);
printf("Thread safety passed!\n");
}

View File

@@ -0,0 +1,319 @@
module FileBucket;
private import tango.io.FilePath,
tango.io.FileConduit;
private import tango.core.Exception;
/******************************************************************************
FileBucket implements a simple mechanism to store and recover a
large quantity of data for the duration of the hosting process.
It is intended to act as a local-cache for a remote data-source,
or as a spillover area for large in-memory cache instances.
Note that any and all stored data is rendered invalid the moment
a FileBucket object is garbage-collected.
The implementation follows a fixed-capacity record scheme, where
content can be rewritten in-place until said capacity is reached.
At such time, the altered content is moved to a larger capacity
record at end-of-file, and a hole remains at the prior location.
These holes are not collected, since the lifespan of a FileBucket
is limited to that of the host process.
All index keys must be unique. Writing to the FileBucket with an
existing key will overwrite any previous content. What follows
is a contrived example:
---
char[] text = "this is a test";
auto bucket = new FileBucket (new FilePath("bucket.bin"), FileBucket.HalfK);
// insert some data, and retrieve it again
bucket.put ("a key", text);
char[] b = cast(char[]) bucket.get ("a key");
assert (b == text);
bucket.close;
---
******************************************************************************/
class FileBucket
{
/**********************************************************************
Define the capacity (block-size) of each record
**********************************************************************/
struct BlockSize
{
int capacity;
}
// basic capacity for each record
private FilePath path;
// basic capacity for each record
private BlockSize block;
// where content is stored
private FileConduit file;
// pointers to file records
private Record[char[]] map;
// current file size
private long fileSize;
// current file usage
private long waterLine;
// supported block sizes
public static const BlockSize EighthK = {128-1},
HalfK = {512-1},
OneK = {1024*1-1},
TwoK = {1024*2-1},
FourK = {1024*4-1},
EightK = {1024*8-1},
SixteenK = {1024*16-1},
ThirtyTwoK = {1024*32-1},
SixtyFourK = {1024*64-1};
/**********************************************************************
Construct a FileBucket with the provided path and record-
size. Selecting a record size that roughly matches the
serialized content will limit 'thrashing'.
**********************************************************************/
this (char[] path, BlockSize block)
{
this (new FilePath(path), block);
}
/**********************************************************************
Construct a FileBucket with the provided path, record-size,
and inital record count. The latter causes records to be
pre-allocated, saving a certain amount of growth activity.
Selecting a record size that roughly matches the serialized
content will limit 'thrashing'.
**********************************************************************/
this (FilePath path, BlockSize block, uint initialRecords = 100)
{
this.path = path;
this.block = block;
// open a storage file
file = new FileConduit (path, FileConduit.ReadWriteCreate);
// set initial file size (can be zero)
fileSize = initialRecords * block.capacity;
file.seek (fileSize);
file.truncate ();
}
/**********************************************************************
Return the block-size in use for this FileBucket
**********************************************************************/
int getBufferSize ()
{
return block.capacity+1;
}
/**********************************************************************
Return where the FileBucket is located
**********************************************************************/
FilePath getFilePath ()
{
return path;
}
/**********************************************************************
Return the currently populated size of this FileBucket
**********************************************************************/
synchronized long length ()
{
return waterLine;
}
/**********************************************************************
Return the serialized data for the provided key. Returns
null if the key was not found.
**********************************************************************/
synchronized void[] get (char[] key)
{
Record r = null;
if (key in map)
{
r = map [key];
return r.read (this);
}
return null;
}
/**********************************************************************
Remove the provided key from this FileBucket.
**********************************************************************/
synchronized void remove (char[] key)
{
map.remove(key);
}
/**********************************************************************
Write a serialized block of data, and associate it with
the provided key. All keys must be unique, and it is the
responsibility of the programmer to ensure this. Reusing
an existing key will overwrite previous data.
Note that data is allowed to grow within the occupied
bucket until it becomes larger than the allocated space.
When this happens, the data is moved to a larger bucket
at the file tail.
**********************************************************************/
synchronized void put (char[] key, void[] data)
{
Record* r = key in map;
if (r is null)
{
auto rr = new Record;
map [key] = rr;
r = &rr;
}
r.write (this, data, block);
}
/**********************************************************************
Close this FileBucket -- all content is lost.
**********************************************************************/
synchronized void close ()
{
if (file)
{
file.detach;
file = null;
map = null;
}
}
/**********************************************************************
Each Record takes up a number of 'pages' within the file.
The size of these pages is determined by the BlockSize
provided during FileBucket construction. Additional space
at the end of each block is potentially wasted, but enables
content to grow in size without creating a myriad of holes.
**********************************************************************/
private static class Record
{
private long offset;
private int length,
capacity = -1;
/**************************************************************
**************************************************************/
private static void eof (FileBucket bucket)
{
throw new IOException ("Unexpected EOF in FileBucket '"~bucket.path.toString()~"'");
}
/**************************************************************
This should be protected from thread-contention at
a higher level.
**************************************************************/
void[] read (FileBucket bucket)
{
void[] data = new ubyte [length];
bucket.file.seek (offset);
if (bucket.file.read (data) != length)
eof (bucket);
return data;
}
/**************************************************************
This should be protected from thread-contention at
a higher level.
**************************************************************/
void write (FileBucket bucket, void[] data, BlockSize block)
{
length = data.length;
// create new slot if we exceed capacity
if (length > capacity)
createBucket (bucket, length, block);
// locate to start of content
bucket.file.seek (offset);
// write content
if (bucket.file.write (data) != length)
eof (bucket);
}
/**************************************************************
**************************************************************/
void createBucket (FileBucket bucket, int bytes, BlockSize block)
{
offset = bucket.waterLine;
capacity = (bytes + block.capacity) & ~block.capacity;
bucket.waterLine += capacity;
if (bucket.waterLine > bucket.fileSize)
{
// grow the filesize
bucket.fileSize = bucket.waterLine * 2;
// expand the physical file size
bucket.file.seek (bucket.fileSize);
bucket.file.truncate ();
}
}
}
}

View File

@@ -0,0 +1,85 @@
private import tango.io.protocol.Reader,
tango.io.protocol.Writer,
tango.io.FileConduit;
/*******************************************************************************
Use cascading reads & writes to handle a composite class. There is
just one primary call for output, and just one for input, but the
classes propogate the request as appropriate.
Note that the class instances don't know how their content will be
represented; that is dictated by the caller (via the reader/writer
implementation).
Note also that this only serializes the content. To serialize the
classes too, take a look at the Pickle.d example.
*******************************************************************************/
void main()
{
// define a serializable class (via interfaces)
class Wumpus : IReadable, IWritable
{
private int a = 11,
b = 112,
c = 1024;
void read (IReader input)
{
input (a) (b) (c);
}
void write (IWriter output)
{
output (a) (b) (c);
}
}
// define a serializable class (via interfaces)
class Wombat : IReadable, IWritable
{
private Wumpus wumpus;
private char[] x = "xyz";
private bool y = true;
private float z = 3.14159;
this (Wumpus wumpus)
{
this.wumpus = wumpus;
}
void read (IReader input)
{
input (x) (y) (z) (wumpus);
}
void write (IWriter output)
{
output (x) (y) (z) (wumpus);
}
}
// construct a Wombat
auto wombat = new Wombat (new Wumpus);
// open a file for IO
auto file = new FileConduit ("random.bin", FileConduit.ReadWriteCreate);
// construct reader & writer upon the file, with binary IO
auto output = new Writer (file);
auto input = new Reader (file);
// write both Wombat & Wumpus (and flush them)
output (wombat) ();
// rewind to file start
file.seek (0);
// read both back again
input (wombat);
}

View File

@@ -0,0 +1,30 @@
private import tango.io.Console,
tango.io.FileScan,
tango.io.FileConst;
/*******************************************************************************
This example sweeps a named sub-directory tree for html files,
and moves them to the current directory. The existing directory
hierarchy is flattened into a naming scheme where a '.' is used
to replace the traditional path-separator
Used by the Tango project to help manage renderings of the source
code.
*******************************************************************************/
void main(char[][] args)
{
// sweep all html files in the specified subdir
if (args.length is 2)
foreach (proxy; (new FileScan).sweep(args[1], ".html").files)
{
auto other = new FilePath (proxy.toString);
proxy.rename (other.replace (FileConst.PathSeparatorChar, '.'));
}
else
Cout ("usage is filebubbler subdir").newline;
}

View File

@@ -0,0 +1,26 @@
private import tango.io.Console,
tango.io.FileConduit;
/*******************************************************************************
Concatenate a number of files onto a single destination
*******************************************************************************/
void main(char[][] args)
{
if (args.length > 2)
{
// open the file for writing
auto dst = new FileConduit (args[1], FileConduit.WriteCreate);
// copy each file onto dst
foreach (char[] arg; args[2..args.length])
dst.copy (new FileConduit(arg));
// flush output and close
dst.close;
}
else
Cout ("usage: filecat target source1 ... sourceN");
}

View File

@@ -0,0 +1,23 @@
private import tango.io.Console,
tango.io.FileConduit;
/*******************************************************************************
open a file, and stream directly to console
*******************************************************************************/
void main (char[][] args)
{
if (args.length is 2)
{
// open a file for reading
auto fc = new FileConduit (args[1]);
// stream directly to console
Cout.stream.copy (fc);
}
else
Cout ("usage is: filecopy filename").newline;
}

View File

@@ -0,0 +1,26 @@
/*****************************************************
Example that shows some simple file operations
Put into public domain by Lars Ivar Igesund
*****************************************************/
import tango.io.Stdout;
import tango.io.FilePath;
void main (char[][] args)
{
auto src = args[0] ~ ".d";
auto dst = new FilePath (args[0] ~ ".d.copy");
Stdout.formatln ("copy file {} to {}", src, dst);
dst.copy (src);
assert (dst.exists);
Stdout.formatln ("removing file {}", dst);
dst.remove;
assert (dst.exists is false);
}

View File

@@ -0,0 +1,11 @@
import tango.io.Console;
import tango.io.FilePath;
void main(){
Cout ((new FilePath(r"d:\path\foo.bat")).name).newline;
Cout ((new FilePath(r"d:\path.two\bar")).name).newline;
Cout ((new FilePath("/home/user.name/bar.")).name).newline;
Cout ((new FilePath(r"d:\path.two\bar")).name).newline;
Cout ((new FilePath("/home/user/.resource")).name).newline;
}

View File

@@ -0,0 +1,32 @@
private import tango.io.Stdout,
tango.io.FileScan;
/*******************************************************************************
List ".d" files and enclosing folders visible via a directory given
as a command-line argument. In this example we're also postponing a
flush on Stdout until output is complete. Stdout is usually flushed
on each invocation of newline or formatln, but here we're using '\n'
to illustrate how to avoid flushing many individual lines
*******************************************************************************/
void main(char[][] args)
{
char[] root = args.length < 2 ? "." : args[1];
Stdout.formatln ("Scanning '{}'", root);
auto scan = (new FileScan)(root, ".d");
Stdout.format ("\n{} Folders\n", scan.folders.length);
foreach (folder; scan.folders)
Stdout.format ("{}\n", folder);
Stdout.format ("\n{0} Files\n", scan.files.length);
foreach (file; scan.files)
Stdout.format ("{}\n", file);
Stdout.formatln ("\n{} Errors", scan.errors.length);
foreach (error; scan.errors)
Stdout (error).newline;
}

View File

@@ -0,0 +1,40 @@
/**************************************************************
Example that use FileScan and Regex as a filter.
Put into public domain by Lars Ivar Igesund
**************************************************************/
import tango.io.File,
tango.io.Stdout,
tango.io.FileScan,
tango.text.Regex;
void main(char[][] args) {
uint total;
if (args.length < 2) {
Stdout("Please pass a directory to search").newline;
return;
}
scope scan = new FileScan;
scope regex = Regex(r"\.(d|obj)$");
scan(args[1], delegate bool (FilePath fp, bool isDir) {
++total;
return isDir || regex.test(fp.toString);
});
foreach (file; scan.files)
Stdout(file).newline;
Stdout.formatln("Found {} matches in {} entries", scan.files.length, total);
Stdout.formatln ("\n{} Errors", scan.errors.length);
foreach (error; scan.errors)
Stdout (error).newline;
}

View File

@@ -0,0 +1,29 @@
private import tango.io.Console,
tango.io.FileConduit;
private import tango.text.stream.LineIterator;
/*******************************************************************************
Read a file line-by-line, sending each one to the console. This
illustrates how to bind a conduit to a stream iterator (iterators
also support the binding of a buffer). Note that stream iterators
are templated for char, wchar and dchar types.
*******************************************************************************/
void main (char[][] args)
{
if (args.length is 2)
{
// open a file for reading
scope file = new FileConduit (args[1]);
// process file one line at a time
foreach (line; new LineIterator!(char)(file))
Cout (line).newline;
}
else
Cout ("usage: lineio filename").newline;
}

View File

@@ -0,0 +1,24 @@
private import tango.io.Console,
tango.io.FileConduit,
tango.io.MappedBuffer;
/*******************************************************************************
open a file, map it into memory, and copy to console
*******************************************************************************/
void main (char[][] args)
{
if (args.length is 2)
{
// open a file for reading
auto mmap = new MappedBuffer (new FileConduit (args[1]));
// copy content to console
Cout (cast(char[]) mmap.slice) ();
}
else
Cout ("usage is: mmap filename").newline;
}

View File

@@ -0,0 +1,13 @@
/*****************************************************
How to create a path with all ancestors
*****************************************************/
import tango.io.FilePath;
void main (char[][] args)
{
auto path = new FilePath (r"d/tango/foo/bar/wumpus");
assert (path.create.exists && path.isFolder);
}

View File

@@ -0,0 +1,38 @@
private import tango.io.FileConduit;
private import tango.io.protocol.Reader,
tango.io.protocol.Writer;
/*******************************************************************************
Create a file for random access. Write some stuff to it, rewind to
file start and read back.
*******************************************************************************/
void main()
{
// open a file for reading
auto fc = new FileConduit ("random.bin", FileConduit.ReadWriteCreate);
// construct (binary) reader & writer upon this conduit
auto read = new Reader (fc);
auto write = new Writer (fc);
int x=10, y=20;
// write some data and flush output since IO is buffered
write (x) (y) ();
// rewind to file start
fc.seek (0);
// read data back again, but swap destinations
read (y) (x);
assert (y is 10);
assert (x is 20);
fc.close();
}

View File

@@ -0,0 +1,25 @@
private import tango.io.Console,
tango.io.UnicodeFile;
/*******************************************************************************
Open a unicode file of an unknown encoding, and converts to UTF-8
for console display. UnicodeFile is templated for char/wchar/dchar
target encodings
*******************************************************************************/
void main (char[][] args)
{
if (args.length is 2)
{
// open a file for reading
auto file = new UnicodeFile!(char) (args[1], Encoding.Unknown);
// display on console
Cout (file.read).newline;
}
else
Cout ("usage is: unifile filename").newline;
}

View File

@@ -0,0 +1,17 @@
private import tango.stdc.stdio;
private import tango.io.Console;
/*******************************************************************************
Demonstrates buffered output. Console output (and Stdout etc) is
buffered, requiring a flush or newline to render on the console.
*******************************************************************************/
void main (char[][] args)
{
Cout ("how now ");
printf ("printf\n");
Cout ("brown cow").newline;
}

View File

@@ -0,0 +1,21 @@
/*******************************************************************************
Hello World using tango.io
This illustrates bare console output, with no fancy formatting.
Console I/O in Tango is UTF-8 across both linux and Win32. The
conversion between various unicode representations is handled
by higher level constructs, such as Stdout and Stderr
Note that Cerr is tied to the console error output, and Cin is
tied to the console input.
*******************************************************************************/
import tango.io.Console;
void main()
{
Cout ("hello, sweetheart \u263a").newline;
}

View File

@@ -0,0 +1,14 @@
/*******************************************************************************
Illustrates the basic console formatting. This is different than
the use of tango.io.Console, in that Stdout supports a variety of
printf-style formatting, and has unicode-conversion support
*******************************************************************************/
private import tango.io.Stdout;
void main()
{
Stdout ("hello, sweetheart \u263a").newline;
}

35
tango/example/dsss.conf Normal file
View File

@@ -0,0 +1,35 @@
[concurrency/fiber_test.d]
[conduits/composite.d]
[conduits/filebubbler.d]
[conduits/filecat.d]
[conduits/filecopy.d]
[conduits/fileops.d]
[conduits/filepathname.d]
[conduits/filescan.d]
[conduits/filescanregex.d]
[conduits/lineio.d]
[conduits/mmap.d]
[conduits/paths.d]
[conduits/randomio.d]
[conduits/unifile.d]
[console/hello.d]
[console/stdout.d]
[logging/chainsaw.d]
[logging/logging.d]
[logging/multilog.d]
[manual/chapterStorage.d]
[networking/homepage.d]
[networking/httpget.d]
[networking/selector.d]
[networking/sockethello.d]
[networking/socketserver.d]
[system/arguments.d]
[system/localtime.d]
[system/normpath.d]
[system/process.d]
[text/formatalign.d]
[text/formatindex.d]
[text/formatspec.d]
[text/localetime.d]
[text/properties.d]
[text/token.d]

42
tango/example/external/GlueFlectioned.d vendored Normal file
View File

@@ -0,0 +1,42 @@
import tango.core.Exception;
import cn.kuehne.flectioned;
TracedExceptionInfo traceHandler( void* ptr = null )
{
class FlectionedTrace :
TracedExceptionInfo
{
this( void* ptr = null )
{
if( ptr )
m_trace = Trace.getTrace( cast(size_t) ptr );
else
m_trace = Trace.getTrace();
}
int opApply( int delegate( inout char[] ) dg )
{
int ret = 0;
foreach( t; m_trace )
{
char[] buf = t.toString;
ret = dg( buf );
if( ret != 0 )
break;
}
return ret;
}
private:
Trace[] m_trace;
}
return new FlectionedTrace( ptr );
}
static this()
{
setTraceHandler( &traceHandler );
}

View File

@@ -0,0 +1,76 @@
@rem ###########################################################################
@rem # CONCURRENCY EXAMPLES
@rem ###########################################################################
@jake concurrency\fiber_test.d -I.. -op -unittest
@rem ###########################################################################
@rem # CONDUIT EXAMPLES
@rem ###########################################################################
@jake conduits\composite.d -I.. -op
@jake conduits\filebubbler.d -I.. -op
@jake -c conduits\filebucket.d -I.. -op
@jake conduits\filecat.d -I.. -op
@jake conduits\filecopy.d -I.. -op
@jake conduits\filepathname.d -I.. -op
@jake conduits\filescan.d -I.. -op
@jake conduits\filescanregex.d -I.. -op
@jake conduits\lineio.d -I.. -op
@jake conduits\mmap.d -I.. -op
@jake conduits\randomio.d -I.. -op
@jake conduits\unifile.d -I.. -op
@rem ###########################################################################
@rem # CONSOLE EXAMPLES
@rem ###########################################################################
@jake console\hello.d -I.. -op
@jake console\stdout.d -I.. -op
@rem ###########################################################################
@rem # LOGGING EXAMPLES
@rem ###########################################################################
@jake logging\chainsaw.d -I.. -op
@jake logging\logging.d -I.. -op
@rem ###########################################################################
@rem # REFERENCE MANUAL EXAMPLES
@rem ###########################################################################
@jake manual\chapterStorage.d -I.. -op
@rem ###########################################################################
@rem # NETWORKING EXAMPLES
@rem ###########################################################################
@jake networking\homepage.d -I.. -op
@jake networking\httpget.d -I.. -op
@jake networking\sockethello.d -I.. -op
@jake networking\socketserver.d -I.. -op
@jake networking\selector.d -I.. -op
@rem ###########################################################################
@rem # SYSTEM EXAMPLES
@rem ###########################################################################
@jake system\argparser.d -I.. -op
@jake system\localtime.d -I.. -op
@jake system\normpath.d -I.. -op
@jake system\process.d -I.. -op
@rem ###########################################################################
@rem # TEXT EXAMPLES
@rem ###########################################################################
@jake text\formatalign.d -I.. -op
@jake text\formatindex.d -I.. -op
@jake text\formatspec.d -I.. -op
@jake text\localetime.d -I.. -op
@jake text\token.d -I.. -op
@rem FINI
@del *.map
@dir *.exe

89
tango/example/linux.mak Normal file
View File

@@ -0,0 +1,89 @@
# Makefile to build the examples of tango for Linux
# Designed to work with GNU make
# Targets:
# make
# Same as make all
# make all
# Build all examples
#
# make <executable-name>
# Build a specified example
# make clean
# remove all build examples
#
#
# Relative path to the tango include dir
# This is where the tango tree is located
TANGO_DIR = ..
# The build tool executable from dsource.org/projects/build
BUILDTOOL = bud
BUILDOPTS = -noautoimport -op -clean -full -g -debug -I$(TANGO_DIR)
.PHONY: all clean
# Standard target
all :
# networking/httpserver \
# networking/servlets \
# networking/servletserver\
SIMPLE_EXAMPLES =\
concurrency/fiber_test \
conduits/FileBucket \
conduits/composite \
conduits/filebubbler \
conduits/filecat \
conduits/filecopy \
conduits/fileops \
conduits/filepathname \
conduits/filescan \
conduits/filescanregex \
conduits/lineio \
conduits/mmap \
conduits/randomio \
conduits/unifile \
console/hello \
console/stdout \
logging/chainsaw \
logging/logging \
networking/homepage \
networking/httpget \
networking/sockethello \
networking/socketserver \
system/argparser \
system/localtime \
system/normpath \
system/process \
networking/selector \
text/formatalign \
text/formatindex \
text/formatspec \
text/localetime \
text/properties \
text/token
REFERENCE_EXAMPLES = \
./reference/chapter4 \
./reference/chapter11
$(SIMPLE_EXAMPLES) : % : %.d
@echo "Building : " $@
$(BUILDTOOL) $< $(BUILDOPTS) -T$@ -unittest
$(REFERENCE_EXAMPLES) : % : %.d
@echo "Building : " $@
$(BUILDTOOL) $< $(BUILDOPTS) -T$@
all : $(SIMPLE_EXAMPLES)
clean :
@echo "Removing all examples"
rm -f $(SIMPLE_EXAMPLES) $(REFERENCE_EXAMPLES)
rm -f conduits/random.bin

View File

@@ -0,0 +1,30 @@
import tango.core.Thread;
import tango.util.log.Log,
tango.util.log.Log4Layout,
tango.util.log.SocketAppender;
import tango.net.InternetAddress;
/*******************************************************************************
Hooks up to Chainsaw for remote log capture. Chainsaw should be
configured to listen with an XMLSocketReciever
*******************************************************************************/
void main()
{
// get a logger to represent this module
auto logger = Log.getLogger ("example.chainsaw");
// hook up an appender for XML output
logger.addAppender (new SocketAppender (new InternetAddress("127.0.0.1", 4448), new Log4Layout));
while (true)
{
logger.info ("Hello Chainsaw!");
Thread.sleep (1.0);
}
}

View File

@@ -0,0 +1,322 @@
/*******************************************************************************
copyright: Copyright (c) 2007 Stonecobra. All rights reserved
license: BSD style: $(LICENSE)
version: Initial release: December 2007
author: stonecobra
*******************************************************************************/
module context;
import tango.core.Thread,
tango.util.log.Log,
tango.util.log.Event,
tango.util.log.EventLayout,
tango.util.log.ConsoleAppender;
import tango.util.log.model.IHierarchy;
/*******************************************************************************
Allows the dynamic setting of log levels on a per-thread basis.
Imagine that a user request comes into your production threaded
server. You can't afford to turn logging up to trace for the sake
of debugging this one users problem, but you also can't afford to
find the problem and fix it. So now you just set the override log
level to TRACE for the thread the user is on, and you get full trace
output for only that user.
*******************************************************************************/
class ThreadLocalDiagnosticContext : IHierarchy.Context
{
private ThreadLocal!(DCData) dcData;
private char[128] tmp;
/***********************************************************************
***********************************************************************/
public this()
{
dcData = new ThreadLocal!(DCData);
}
/***********************************************************************
set the 'diagnostic' Level for logging. This overrides
the Level in the current Logger. The default level starts
at NONE, so as not to modify the behavior of existing clients
of tango.util.log
***********************************************************************/
void setLevel (ILevel.Level level)
{
auto data = dcData.val;
data.level = level;
dcData.val = data;
}
/***********************************************************************
All log appends will be checked against this to see if a
log level needs to be temporarily adjusted.
***********************************************************************/
bool isEnabled (ILevel.Level setting, ILevel.Level level = ILevel.Level.Trace)
{
return level >= setting || level >= dcData.val.level;
}
/***********************************************************************
Return the label to use for the current log message. Usually
called by the Layout. This implementation returns "{}".
***********************************************************************/
char[] label ()
{
return dcData.val.getLabel;
}
/***********************************************************************
Push another string into the 'stack'. This strings will be
appened together when getLabel is called.
***********************************************************************/
void push (char[] label)
{
auto data = dcData.val;
data.push(label);
dcData.val = data;
}
/***********************************************************************
pop the current label off the stack.
***********************************************************************/
void pop ()
{
auto data = dcData.val;
data.pop;
dcData.val = data;
}
/***********************************************************************
Clear the label stack.
***********************************************************************/
void clear()
{
auto data = dcData.val;
data.clear;
dcData.val = data;
}
}
/*******************************************************************************
The thread locally stored struct to hold the logging level and
the label stack.
*******************************************************************************/
private struct DCData {
ILevel.Level level = ILevel.Level.None;
char[][8] stack;
bool shouldUpdate = true;
int stackIndex = 0;
uint labelLength;
char[256] labelContent;
char[] getLabel() {
if (shouldUpdate) {
labelLength = 0;
append(" {");
for (int i = 0; i < stackIndex; i++) {
append(stack[i]);
if (i < stackIndex - 1) {
append(" ");
}
}
append("}");
shouldUpdate = false;
}
return labelContent[0..labelLength];
}
void append(char[] x) {
uint addition = x.length;
uint newLength = labelLength + x.length;
if (newLength < labelContent.length)
{
labelContent [labelLength..newLength] = x[0..addition];
labelLength = newLength;
}
}
void push(char[] label) {
shouldUpdate = true;
stack[stackIndex] = label.dup;
stackIndex++;
}
void pop() {
shouldUpdate = true;
if (stackIndex > 0) {
stack[stackIndex] = null;
stackIndex--;
}
}
void clear() {
shouldUpdate = true;
for (int i = 0; i < stack.length; i++) {
stack[i] = null;
}
}
}
/*******************************************************************************
Simple console appender that counts the number of log lines it
has written.
*******************************************************************************/
class TestingConsoleAppender : ConsoleAppender {
int events = 0;
this (EventLayout layout = null)
{
super(layout);
}
override void append (Event event)
{
events++;
super.append(event);
}
}
/*******************************************************************************
Testing harness for the DiagnosticContext functionality.
*******************************************************************************/
void main(char[][] args)
{
//set up our appender that counts the log output. This is the configuration
//equivalent of importing tango.util.log.Configurator.
auto appender = new TestingConsoleAppender(new SimpleTimerLayout);
Log.getRootLogger.addAppender(appender);
char[128] tmp = 0;
auto log = Log.getLogger("context");
log.setLevel(log.Level.Info);
//first test, use all defaults, validating it is working. None of the trace()
//calls should count in the test.
for (int i=0;i < 10; i++) {
log.info(log.format(tmp, "test1 {}", i));
log.trace(log.format(tmp, "test1 {}", i));
}
if (appender.events !is 10) {
log.error(log.format(tmp, "events:{}", appender.events));
throw new Exception("Incorrect Number of events in normal mode");
}
appender.events = 0;
//test the thread local implementation without any threads, as a baseline.
//should be same result as test1
auto context = new ThreadLocalDiagnosticContext;
Log.getHierarchy.context(context);
for (int i=0;i < 10; i++) {
log.info(log.format(tmp, "test2 {}", i));
log.trace(log.format(tmp, "test2 {}", i));
}
if (appender.events !is 10) {
log.error(log.format(tmp, "events:{}", appender.events));
throw new Exception("Incorrect Number of events in TLS single thread mode");
}
appender.events = 0;
//test the thread local implementation without any threads, as a baseline.
//This should count all logging requests, because the DiagnosticContext has
//'overridden' the logging level on ALL loggers up to TRACE.
context.setLevel(log.Level.Trace);
for (int i=0;i < 10; i++) {
log.info(log.format(tmp, "test3 {}", i));
log.trace(log.format(tmp, "test3 {}", i));
}
if (appender.events !is 20) {
log.error(log.format(tmp, "events:{}", appender.events));
throw new Exception("Incorrect Number of events in TLS single thread mode with level set");
}
appender.events = 0;
//test the thread local implementation without any threads, as a baseline.
context.setLevel(log.Level.None);
for (int i=0;i < 10; i++) {
log.info(log.format(tmp, "test4 {}", i));
log.trace(log.format(tmp, "test4 {}", i));
}
if (appender.events !is 10) {
log.error(log.format(tmp, "events:{}", appender.events));
throw new Exception("Incorrect Number of events in TLS single thread mode after level reset");
}
//Now test threading. set up a trace context in one thread, with a label, while
//keeping the second thread at the normal configuration.
appender.events = 0;
ThreadGroup tg = new ThreadGroup();
tg.create({
char[128] tmp = 0;
context.setLevel(log.Level.Trace);
context.push("specialthread");
context.push("2ndlevel");
for (int i=0;i < 10; i++) {
log.info(log.format(tmp, "test5 {}", i));
log.trace(log.format(tmp, "test5 {}", i));
}
});
tg.create({
char[128] tmp = 0;
context.setLevel(log.Level.None);
for (int i=0;i < 10; i++) {
log.info(log.format(tmp, "test6 {}", i));
log.trace(log.format(tmp, "test6 {}", i));
}
});
tg.joinAll();
if (appender.events !is 30) {
log.error(log.format(tmp, "events:{}", appender.events));
throw new Exception("Incorrect Number of events in TLS multi thread mode");
}
}

View File

@@ -0,0 +1,102 @@
/*******************************************************************************
Shows how the basic functionality of Logger operates.
*******************************************************************************/
private import tango.util.log.Log,
tango.util.log.Configurator;
/*******************************************************************************
Search for a set of prime numbers
*******************************************************************************/
void compute (Logger log, uint max)
{
byte* feld;
int teste=1,
mom,
hits=0,
s=0,
e = 1;
int count;
char tmp[128] = void;
void set (byte* f, uint x)
{
*(f+(x)/16) |= 1 << (((x)%16)/2);
}
byte test (byte* f, uint x)
{
return cast(byte) (*(f+(x)/16) & (1 << (((x)%16)/2)));
}
// information level
log.info (log.format (tmp, "Searching prime numbers up to {}", max));
feld = (new byte[max / 16 + 1]).ptr;
// get milliseconds since application began
auto begin = log.runtime;
while ((teste += 2) < max)
if (! test (feld, teste))
{
if ((++hits & 0x0f) == 0)
// more information level
log.info (log.format (tmp, "found {}", hits));
for (mom=3*teste; mom < max; mom += teste<<1)
set (feld, mom);
}
// get number of milliseconds we took to compute
auto period = log.runtime - begin;
if (hits)
// more information
log.info (log.format (tmp, "{} prime numbers found in {} millsecs", hits, period));
else
// a warning level
log.warn ("no prime numbers found");
// check to see if we're enabled for
// tracing before we expend a lot of effort
if (log.isEnabled (log.Level.Trace))
{
e = max;
count = 0 - 2;
if (s % 2 is 0)
count++;
while ((count+=2) < e)
// log trace information
if (! test (feld, count))
log.trace (log.format (tmp, "prime found: {}", count));
}
}
/*******************************************************************************
Compute a bunch of prime numbers
*******************************************************************************/
void main()
{
// get a logger to represent this module. We could just as
// easily share a name with some other module(s)
auto log = Log.getLogger ("example.logging");
try {
compute (log, 1000);
} catch (Exception x)
{
// log the exception as an error
log.error ("Exception: " ~ x.toString);
}
}

View File

@@ -0,0 +1,31 @@
import tango.util.log.Log;
import tango.util.log.Log4Layout;
import tango.util.log.FileAppender;
import tango.util.log.ConsoleAppender;
import tango.util.log.RollingFileAppender;
/*******************************************************************************
Shows how to setup multiple appenders on logging tree
*******************************************************************************/
void main ()
{
// set default logging level at the root
auto log = Log.getRootLogger;
log.setLevel (log.Level.Trace);
// 10 logs, all with 10 mbs each
log.addAppender (new RollingFileAppender("rolling.log", 9, 1024*1024*10));
// a single file appender, with an XML layout
log.addAppender (new FileAppender ("single.log", new Log4Layout));
// console appender
log.addAppender (new ConsoleAppender);
// log to all
log.trace ("three-way logging");
}

View File

@@ -0,0 +1,237 @@
module example.reference.chapter11;
import tango.util.collection.HashMap;
import tango.util.collection.ArrayBag;
import tango.util.collection.LinkSeq;
import tango.util.collection.CircularSeq;
import tango.util.collection.ArraySeq;
import tango.util.collection.TreeBag;
import tango.util.collection.iterator.FilteringIterator;
import tango.util.collection.iterator.InterleavingIterator;
import tango.io.Stdout;
import tango.core.Exception;
import tango.util.collection.model.Comparator;
import tango.util.collection.impl.BagCollection;
import tango.util.collection.impl.SeqCollection;
import Ascii = tango.text.Ascii;
void linkedListExample(){
Stdout.format( "linkedListExample" ).newline;
alias LinkSeq!(char[]) StrList;
StrList lst = new StrList();
lst.append( "value1" );
lst.append( "value2" );
lst.append( "value3" );
auto it = lst.elements();
// The call to .more gives true, if there are more elements
// and switches the iterator to the next one if available.
while( it.more ){
char[] item_value = it.get;
Stdout.format( "Value:{0}", item_value ).newline;
}
}
void hashMapExample(){
Stdout.format( "hashMapExample" ).newline;
alias HashMap!(char[], char[]) StrStrMap;
StrStrMap map = new StrStrMap();
map.add( "key1", "value1" );
char[] key = "key1";
Stdout.format( "Key: {0}, Value:{1}", key, map.get( key )).newline;
auto it = map.keys();
// The call to .more gives true, if there are more elements
// and switches the iterator to the next one if available.
while( it.more ){
char[] item_key = void;
char[] item_value = it.get( item_key ); // only for maps, the key is returns via inout
Stdout.format( "Key: {0}, Value:{1}", item_key, item_value ).newline;
}
}
void testComparator(){
char[][] result;
// Create and fill the containers
auto nameSet = new TreeBag!(char[])( null, new class() Comparator!(char[]){
int compare( char[] first, char[] second ){
return Ascii.icompare( first, second );
}
});
nameSet.addIf( "Alice" );
nameSet.addIf( "Bob" );
nameSet.addIf( "aliCe" );
// use foreach to iterate over the container
foreach ( char[] i; nameSet )
result ~= i;
foreach( char[] i; result ) Stdout.format( "{0} ", i );
Stdout.newline;
// test the result
assert( result == [ "Alice", "Bob" ], "testIterator" );
}
void testSreener(){
int[] result;
// Create and fill the containers
auto ratioSamples = new ArraySeq!(float)( (float v){
return v >= 0.0f && v < 1.0f;
});
ratioSamples.append( 0.0f );
ratioSamples.append( 0.5f );
ratioSamples.append( 0.99999f );
// try to insert a value that is not allowed
try{
ratioSamples.append( 1.0f );
// will never get here
assert( false );
} catch( IllegalElementException e ){
}
}
void testForeach(){
int[] result;
// Create and fill the containers
auto l1 = new CircularSeq!(int);
for( int i = 0; i < 6; i+=2 ){
l1.append( i );
}
// use foreach to iterate over the container
foreach ( int i; l1 )
result ~= i;
// foreach_reverse ( int i; l1 )
// result ~= i;
// test the result
assert( result == [ 0, 2, 4 ], "testIterator" );
}
void testIterator(){
int[] result;
// Create and fill the containers
auto l1 = new LinkSeq!(int);
for( int i = 0; i < 6; i+=2 ){
l1.append( i );
}
// define the Iterator
auto it = l1.elements();
// use the Iterator to iterate over the container
while (it.more())
result ~= it.get();
// test the result
assert( result == [ 0, 2, 4 ], "testIterator" );
}
void testFilteringIterator(){
int[] result;
alias ArrayBag!(int) IntBag;
// Create and fill the container
auto ib = new IntBag;
for( int i = 0; i < 20; i++ ){
ib.add( i );
}
// define the FilteringIterator with a function literal
auto it = new FilteringIterator!(int)( ib.elements(), (int i){
return i >= 3 && i < 7;
});
// use the Iterator with the more()/get() pattern
while (it.more())
result ~= it.get();
// test the result
assert( result == [ 3, 4, 5, 6 ], "testFilteringIterator" );
}
void testFilteringIteratorRemove(){
int[] result;
// Create and fill the container
auto container = new LinkSeq!(int);
for( int i = 0; i < 10; i++ ){
container.append( i );
}
// 1. Build a list of elements to delete
auto dellist = new LinkSeq!(int);
foreach( int i; container ){
if( i < 3 || i >= 7 ){
// container.remove( i ); /* NOT POSSIBLE */
dellist.append( i );
}
}
// 2. Iterate over this deletion list and
// delete the items in the original container.
foreach( int i; dellist ){
container.remove( i );
}
foreach ( int i; container )
result ~= i;
// test the result
assert( result == [ 3, 4, 5, 6 ], "testFilteringIterator" );
}
void testInterleavingIterator(){
int[] result;
// Create and fill the containers
auto l1 = new LinkSeq!(int);
auto l2 = new ArraySeq!(int);
for( int i = 0; i < 6; i+=2 ){
l1.append( i );
l2.append( i+1 );
}
// define the InterleavingIterator
auto it = new InterleavingIterator!(int)( l1.elements(), l2.elements() );
// use the InterleavingIterator to iterate over the container
while (it.more())
result ~= it.get();
// test the result
assert( result == [ 0, 1, 2, 3, 4, 5 ], "testInterleavingIterator" );
}
// foreach( int i; result ) Stdout.format( "{0} ", i );
// Stdout.newline;
void main(){
Stdout.format( "reference - Chapter 11 Example" ).newline;
hashMapExample();
linkedListExample();
testSreener();
testComparator();
testForeach();
testIterator();
testFilteringIterator();
testInterleavingIterator();
testFilteringIteratorRemove();
Stdout.format( "=== End ===" ).newline;
}

View File

@@ -0,0 +1,30 @@
private import tango.io.Console;
private import tango.net.http.HttpClient,
tango.net.http.HttpHeaders;
/*******************************************************************************
Shows how to use HttpClient to retrieve content from the D website
*******************************************************************************/
void main()
{
auto client = new HttpClient (HttpClient.Get, "http://www.digitalmars.com/d/intro.html");
// open the client and get the input stream
auto input = client.open;
scope (exit)
client.close;
// display returned content on console
if (client.isResponseOK)
Cout.stream.copy (input);
else
Cout ("failed to return the D home page");
// flush the console
Cout.newline;
}

View File

@@ -0,0 +1,27 @@
private import tango.io.Console;
private import tango.net.http.HttpGet;
/*******************************************************************************
Read a page from a website, gathering the entire page before
returning any content. This illustrates a high-level approach
to retrieving web-content, whereas the homepage example shows
a somewhat lower-level approach.
Note that this expects a fully qualified URL (with scheme),
such as "http://www.digitalmars.com/d/intro.html"
*******************************************************************************/
void main (char[][] args)
{
char[] url = (args.length is 2) ? args[1] : "http://www.digitalmars.com/d/intro.html";
// open a web-page for reading (see HttpPost for writing)
auto page = new HttpGet (url);
// retrieve and flush display content
Cout (cast(char[]) page.read) ();
}

View File

@@ -0,0 +1,399 @@
/*******************************************************************************
copyright: Copyright (c) 2006 Juan Jose Comellas. All rights reserved
license: BSD style: $(LICENSE)
author: Juan Jose Comellas <juanjo@comellas.com.ar>
*******************************************************************************/
private
{
version (Posix)
{
import tango.io.selector.PollSelector;
}
else version (linux)
{
import tango.io.selector.EpollSelector;
import tango.sys.linux.linux;
}
import tango.io.selector.model.ISelector;
import tango.io.selector.Selector;
import tango.io.selector.SelectSelector;
import tango.io.selector.SelectorException;
import tango.io.Conduit;
import tango.net.Socket;
import tango.net.SocketConduit;
import tango.net.ServerSocket;
import tango.time.Clock;
import tango.util.log.Log;
import tango.util.log.ConsoleAppender;
import tango.util.log.DateLayout;
import tango.text.convert.Sprint;
import tango.core.Exception;
import tango.core.Thread;
import tango.sys.Common;
import tango.stdc.errno;
}
const uint HANDLE_COUNT = 4;
const uint EVENT_COUNT = 4;
const uint LOOP_COUNT = 50000;
const char[] SERVER_ADDR = "127.0.0.1";
const ushort SERVER_PORT = 4000;
const uint MAX_LENGTH = 16;
int main(char[][] args)
{
Logger log = Log.getLogger("selector");
Sprint!(char) sprint = new Sprint!(char)(256);
log.addAppender(new ConsoleAppender(new DateLayout()));
ISelector selector;
for (int i = 0; i < 1; i++)
{
// Testing the SelectSelector
log.info(sprint("Pass {0}: Testing the select-based selector", i + 1));
selector = new SelectSelector();
testSelector(selector);
}
// Testing the PollSelector
version (Posix)
{
for (int i = 0; i < 1; i++)
{
log.info(sprint("Pass {0}: Testing the poll-based selector", i + 1));
selector = new PollSelector();
testSelector(selector);
}
}
// Testing the EpollSelector
version (linux)
{
for (int i = 0; i < 1; i++)
{
log.info(sprint("Pass {0}: Testing the epoll-based selector", i + 1));
selector = new EpollSelector();
testSelector(selector);
}
}
return 0;
}
/**
* Create a server socket and run the Selector on it.
*/
void testSelector(ISelector selector)
{
Logger log = Log.getLogger("selector.server");
Sprint!(char) sprint = new Sprint!(char)(512);
uint connectCount = 0;
uint receiveCount = 0;
uint sendCount = 0;
uint failedConnectCount = 0;
uint failedReceiveCount = 0;
uint failedSendCount = 0;
uint closeCount = 0;
uint errorCount = 0;
Time start = Clock.now;
Thread clientThread;
selector.open(HANDLE_COUNT, EVENT_COUNT);
clientThread = new Thread(&clientThreadFunc);
clientThread.start();
try
{
TimeSpan timeout = TimeSpan.seconds(1);
InternetAddress addr = new InternetAddress(SERVER_ADDR, SERVER_PORT);
ServerSocket serverSocket = new ServerSocket(addr, 5);
SocketConduit clientSocket;
char[MAX_LENGTH] buffer;
int eventCount;
uint count;
int i = 0;
debug (selector)
log.trace("Registering server socket to Selector");
selector.register(serverSocket, Event.Read);
while (true)
{
debug (selector)
log.trace(sprint("[{0}] Waiting for events from Selector", i));
eventCount = selector.select(timeout);
debug (selector)
log.trace(sprint("[{0}] {1} events received from Selector", i, eventCount));
if (eventCount > 0)
{
foreach (SelectionKey selectionKey; selector.selectedSet())
{
debug (selector)
log.trace(sprint("[{0}] Event mask for socket {1} is 0x{2:x4}",
i, cast(int) selectionKey.conduit.fileHandle(),
cast(uint) selectionKey.events));
if (selectionKey.isReadable())
{
if (selectionKey.conduit is serverSocket)
{
debug (selector)
log.trace(sprint("[{0}] New connection from client", i));
clientSocket = serverSocket.accept();
if (clientSocket !is null)
{
selector.register(clientSocket, Event.Read);
connectCount++;
}
else
{
debug (selector)
log.trace(sprint("[{0}] New connection attempt failed", i));
failedConnectCount++;
}
}
else
{
// Reading from a client socket
debug (selector)
log.trace(sprint("[{0}] Receiving message from client", i));
count = (cast(SocketConduit) selectionKey.conduit).read(buffer);
if (count != IConduit.Eof)
{
debug (selector)
log.trace(sprint("[{0}] Received {1} from client ({2} bytes)",
i, buffer[0..count], count));
selector.reregister(selectionKey.conduit, Event.Write);
receiveCount++;
}
else
{
debug (selector)
log.trace(sprint("[{0}] Handle {1} was closed; removing it from Selector",
i, cast(int) selectionKey.conduit.fileHandle()));
selector.unregister(selectionKey.conduit);
(cast(SocketConduit) selectionKey.conduit).close();
failedReceiveCount++;
continue;
}
}
}
if (selectionKey.isWritable())
{
debug (selector)
log.trace(sprint("[{0}] Sending PONG to client", i));
count = (cast(SocketConduit) selectionKey.conduit).write("PONG");
if (count != IConduit.Eof)
{
debug (selector)
log.trace(sprint("[{0}] Sent PONG to client ({1} bytes)", i, count));
selector.reregister(selectionKey.conduit, Event.Read);
sendCount++;
}
else
{
debug (selector)
log.trace(sprint("[{0}] Handle {1} was closed; removing it from Selector",
i, selectionKey.conduit.fileHandle()));
selector.unregister(selectionKey.conduit);
(cast(SocketConduit) selectionKey.conduit).close();
failedSendCount++;
continue;
}
}
if (selectionKey.isError() || selectionKey.isHangup() || selectionKey.isInvalidHandle())
{
char[] status;
if (selectionKey.isHangup())
{
closeCount++;
status = "Hangup";
}
else
{
errorCount++;
if (selectionKey.isInvalidHandle())
status = "Invalid request";
else
status = "Error";
}
debug (selector)
{
log.trace(sprint("[{0}] {1} in handle {2} from Selector",
i, status, cast(int) selectionKey.conduit.fileHandle()));
log.trace(sprint("[{0}] Unregistering handle {1} from Selector",
i, cast(int) selectionKey.conduit.fileHandle()));
}
selector.unregister(selectionKey.conduit);
(cast(Conduit) selectionKey.conduit).close();
if (selectionKey.conduit !is serverSocket)
{
continue;
}
else
{
break;
}
}
}
}
else
{
debug (selector)
log.trace(sprint("[{0}] No more pending events in Selector; aborting", i));
break;
}
i++;
// Thread.sleep(1.0);
/*
if (i % 100 == 0)
{
fullCollect();
getStats(gc)
}
*/
}
serverSocket.socket().detach;
}
catch (SelectorException e)
{
log.error(sprint("Selector exception caught:\n{0}", e.toString()));
}
catch (Exception e)
{
log.error(sprint("Exception caught:\n{0}", e.toString()));
}
log.info(sprint("Success: connect={0}; recv={1}; send={2}; close={3}",
connectCount, receiveCount, sendCount, closeCount));
log.info(sprint("Failure: connect={0}, recv={1}; send={2}; error={3}",
failedConnectCount, failedReceiveCount, failedSendCount, errorCount));
log.info(sprint("Total time: {0} ms", cast(uint) (Clock.now - start).millis));
clientThread.join();
selector.close;
}
/**
* Thread that creates a client socket and sends messages to the server socket.
*/
void clientThreadFunc()
{
Logger log = Log.getLogger("selector.client");
Sprint!(char) sprint = new Sprint!(char)(256);
SocketConduit socket = new SocketConduit();
Thread.sleep(0.010); // 10 milliseconds
try
{
InternetAddress addr = new InternetAddress(SERVER_ADDR, SERVER_PORT);
char[MAX_LENGTH] buffer;
uint count;
int i;
debug (selector)
log.trace(sprint("[{0}] Connecting to server", i));
socket.connect(addr);
for (i = 1; i <= LOOP_COUNT; i++)
{
debug (selector)
log.trace(sprint("[{0}] Sending PING to server", i));
while (true)
{
try
{
count = socket.write("PING");
break;
}
catch (SocketException e)
{
if (errno != EINTR)
throw e;
}
}
if (count != IConduit.Eof)
{
debug (selector)
{
log.trace(sprint("[{0}] Sent PING to server ({1} bytes)", i, count));
log.trace(sprint("[{0}] Receiving message from server", i));
}
while (true)
{
try
{
count = socket.read(buffer);
break;
}
catch (SocketException e)
{
if (errno != EINTR)
throw e;
}
}
if (count != IConduit.Eof)
{
debug (selector)
log.trace(sprint("[{0}] Received {1} from server ({2} bytes)",
i, buffer[0..count], count));
}
else
{
debug (selector)
log.trace(sprint("[{0}] Handle was closed; aborting",
i, socket.fileHandle()));
break;
}
}
else
{
debug (selector)
log.trace(sprint("[{0}] Handle {1} was closed; aborting",
i, socket.fileHandle()));
break;
}
}
socket.shutdown();
socket.close();
}
catch (Exception e)
{
log.error(sprint("Exception caught:\n{0}", e.toString()));
}
debug (selector)
log.trace("Leaving thread");
return 0;
}

View File

@@ -0,0 +1,29 @@
/*******************************************************************************
Shows how to create a basic socket client, and how to converse with
a remote server. The server must be running for this to succeed
*******************************************************************************/
private import tango.io.Console;
private import tango.net.SocketConduit,
tango.net.InternetAddress;
void main()
{
// make a connection request to the server
auto request = new SocketConduit;
request.connect (new InternetAddress ("localhost", 8080));
request.output.write ("hello\n");
// wait for response (there is an optional timeout supported)
char[64] response;
auto size = request.input.read (response);
// close socket
request.close;
// display server response
Cout (response[0..size]).newline;
}

View File

@@ -0,0 +1,53 @@
/*******************************************************************************
Shows how to create a basic socket server, and how to talk to
it from a socket client. Note that both the server and client
are entirely simplistic, and therefore this is for illustration
purposes only. See HttpServer for something more robust.
*******************************************************************************/
private import tango.core.Thread;
private import tango.io.Console;
private import tango.net.ServerSocket,
tango.net.SocketConduit;
/*******************************************************************************
Create a socket server, and have it respond to a request
*******************************************************************************/
void main()
{
const int port = 8080;
// thread body for socket-listener
void run()
{
auto server = new ServerSocket (new InternetAddress(port));
// wait for requests
auto request = server.accept;
// write a response
request.output.write ("server replies 'hello'");
}
// start server in a separate thread, and wait for it to start
(new Thread (&run)).start;
Thread.sleep (0.250);
// make a connection request to the server
auto request = new SocketConduit;
request.connect (new InternetAddress("localhost", port));
// wait for and display response (there is an optional timeout)
char[64] response;
auto len = request.input.read (response);
Cout (response[0..len]).newline;
request.close;
}

View File

@@ -0,0 +1,138 @@
/*******************************************************************************
copyright: Copyright (c) 2006 Juan Jose Comellas. All rights reserved
license: BSD style: $(LICENSE)
author: Juan Jose Comellas <juanjo@comellas.com.ar>
Converted to use core.sync by Sean Kelly <sean@f4.ca>
*******************************************************************************/
private import tango.core.sync.Barrier;
private import tango.core.sync.Mutex;
private import tango.core.Exception;
private import tango.core.Thread;
private import tango.io.Stdout;
private import tango.text.convert.Integer;
debug (barrier)
{
private import tango.util.log.Log;
private import tango.util.log.ConsoleAppender;
private import tango.util.log.DateLayout;
}
/**
* Example program for the tango.core.sync.Barrier module.
*/
void main(char[][] args)
{
const uint MaxThreadCount = 100;
const uint LoopsPerThread = 10000;
debug (barrier)
{
Logger log = Log.getLogger("barrier");
log.addAppender(new ConsoleAppender(new DateLayout()));
log.info("Barrier test");
}
Barrier barrier = new Barrier(MaxThreadCount);
Mutex mutex = new Mutex();
uint count = 0;
uint correctCount = 0;
void barrierTestThread()
{
debug (barrier)
{
Logger log = Log.getLogger("barrier." ~ Thread.getThis().name());
log.trace("Starting thread");
}
try
{
for (uint i; i < LoopsPerThread; ++i)
{
// 'count' is a resource shared by multiple threads, so we must
// acquire the mutex before modifying it.
synchronized (mutex)
{
// debug (barrier)
// log.trace("Acquired mutex");
count++;
// debug (barrier)
// log.trace("Releasing mutex");
}
}
// We wait for all the threads to finish counting.
debug (barrier)
log.trace("Waiting on barrier");
barrier.wait();
debug (barrier)
log.trace("Barrier was opened");
// We make sure that all the threads exited the barrier after
// *all* of them had finished counting.
synchronized (mutex)
{
// debug (barrier)
// log.trace("Acquired mutex");
if (count == MaxThreadCount * LoopsPerThread)
{
++correctCount;
}
// debug (barrier)
// log.trace("Releasing mutex");
}
}
catch (SyncException e)
{
Stderr.formatln("Sync exception caught in Barrier test thread {0}:\n{1}\n",
Thread.getThis().name, e.toString());
}
catch (Exception e)
{
Stderr.formatln("Unexpected exception caught in Barrier test thread {0}:\n{1}\n",
Thread.getThis().name, e.toString());
}
}
ThreadGroup group = new ThreadGroup();
Thread thread;
char[10] tmp;
for (uint i = 0; i < MaxThreadCount; ++i)
{
thread = new Thread(&barrierTestThread);
thread.name = "thread-" ~ format(tmp, i);
group.add(thread);
debug (barrier)
log.trace("Created thread " ~ thread.name);
thread.start();
}
debug (barrier)
log.trace("Waiting for threads to finish");
group.joinAll();
if (count == MaxThreadCount * LoopsPerThread)
{
debug (barrier)
log.info("The Barrier test was successful");
}
else
{
debug (barrier)
{
log.error("The Barrier is not working properly: the counter has an incorrect value");
assert(false);
}
else
{
assert(false, "The Barrier is not working properly: the counter has an incorrect value");
}
}
}

View File

@@ -0,0 +1,307 @@
/*******************************************************************************
copyright: Copyright (c) 2006 Juan Jose Comellas. All rights reserved
license: BSD style: $(LICENSE)
author: Juan Jose Comellas <juanjo@comellas.com.ar>
Converted to use core.sync by Sean Kelly <sean@f4.ca>
*******************************************************************************/
private import tango.core.sync.Condition;
private import tango.core.Exception;
private import tango.core.Thread;
private import tango.text.convert.Integer;
private import tango.io.Stdout;
debug (condition)
{
private import tango.util.log.Log;
private import tango.util.log.ConsoleAppender;
private import tango.util.log.DateLayout;
}
void main(char[][] args)
{
debug (condition)
{
Logger log = Log.getLogger("condition");
log.addAppender(new ConsoleAppender(new DateLayout()));
log.info("Condition test");
}
testNotifyOne();
testNotifyAll();
}
/**
* Test for Condition.notify().
*/
void testNotifyOne()
{
debug (condition)
{
Logger log = Log.getLogger("condition.notify-one");
}
scope Mutex mutex = new Mutex();
scope Condition cond = new Condition(mutex);
int waiting = 0;
Thread thread;
void notifyOneTestThread()
{
debug (condition)
{
Logger log = Log.getLogger("condition.notify-one." ~ Thread.getThis().name());
log.trace("Starting thread");
}
try
{
synchronized (mutex)
{
debug (condition)
log.trace("Acquired mutex");
scope(exit)
{
debug (condition)
log.trace("Releasing mutex");
}
waiting++;
while (waiting != 2)
{
debug (condition)
log.trace("Waiting on condition variable");
cond.wait();
}
debug (condition)
log.trace("Condition variable was signaled");
}
}
catch (SyncException e)
{
Stderr.formatln("Sync exception caught in Condition test thread {0}:\n{1}",
Thread.getThis().name(), e.toString());
}
catch (Exception e)
{
Stderr.formatln("Unexpected exception caught in Condition test thread {0}:\n{1}",
Thread.getThis().name(), e.toString());
}
debug (condition)
log.trace("Exiting thread");
}
thread = new Thread(&notifyOneTestThread);
thread.name = "thread-1";
debug (condition)
log.trace("Created thread " ~ thread.name);
thread.start();
try
{
// Poor man's barrier: wait until the other thread is waiting.
while (true)
{
synchronized (mutex)
{
if (waiting != 1)
{
Thread.yield();
}
else
{
break;
}
}
}
synchronized (mutex)
{
debug (condition)
log.trace("Acquired mutex");
waiting++;
debug (condition)
log.trace("Notifying test thread");
cond.notify();
debug (condition)
log.trace("Releasing mutex");
}
thread.join();
if (waiting == 2)
{
debug (condition)
log.info("The Condition notification test to one thread was successful");
}
else
{
debug (condition)
{
log.error("The condition variable notification to one thread is not working");
assert(false);
}
else
{
assert(false, "The condition variable notification to one thread is not working");
}
}
}
catch (SyncException e)
{
Stderr.formatln("Sync exception caught in main thread:\n{0}", e.toString());
}
}
/**
* Test for Condition.notifyAll().
*/
void testNotifyAll()
{
const uint MaxThreadCount = 10;
debug (condition)
{
Logger log = Log.getLogger("condition.notify-all");
}
scope Mutex mutex = new Mutex();
scope Condition cond = new Condition(mutex);
int waiting = 0;
/**
* This thread waits for a notification from the main thread.
*/
void notifyAllTestThread()
{
debug (condition)
{
Logger log = Log.getLogger("condition.notify-all." ~ Thread.getThis().name());
log.trace("Starting thread");
}
try
{
synchronized (mutex)
{
debug (condition)
log.trace("Acquired mutex");
waiting++;
while (waiting != MaxThreadCount + 1)
{
debug (condition)
log.trace("Waiting on condition variable");
cond.wait();
}
debug (condition)
log.trace("Condition variable was signaled");
debug (condition)
log.trace("Releasing mutex");
}
}
catch (SyncException e)
{
Stderr.formatln("Sync exception caught in Condition test thread {0}:\n{1}",
Thread.getThis().name(), e.toString());
}
catch (Exception e)
{
Stderr.formatln("Unexpected exception caught in Condition test thread {0}:\n{1}",
Thread.getThis().name(), e.toString());
}
debug (condition)
log.trace("Exiting thread");
}
ThreadGroup group = new ThreadGroup();
Thread thread;
char[10] tmp;
for (uint i = 0; i < MaxThreadCount; ++i)
{
thread = new Thread(&notifyAllTestThread);
thread.name = "thread-" ~ format(tmp, i);
group.add(thread);
debug (condition)
log.trace("Created thread " ~ thread.name);
thread.start();
}
try
{
// Poor man's barrier: wait until all the threads are waiting.
while (true)
{
synchronized (mutex)
{
if (waiting != MaxThreadCount)
{
Thread.yield();
}
else
{
break;
}
}
}
synchronized (mutex)
{
debug (condition)
log.trace("Acquired mutex");
waiting++;
debug (condition)
log.trace("Notifying all threads");
cond.notifyAll();
debug (condition)
log.trace("Releasing mutex");
}
debug (condition)
log.trace("Waiting for threads to finish");
group.joinAll();
if (waiting == MaxThreadCount + 1)
{
debug (condition)
log.info("The Condition notification test to many threads was successful");
}
else
{
debug (condition)
{
log.error("The condition variable notification to many threads is not working");
assert(false);
}
else
{
assert(false, "The condition variable notification to many threads is not working");
}
}
}
catch (SyncException e)
{
Stderr.formatln("Sync exception caught in main thread:\n{0}", e.toString());
}
}

View File

@@ -0,0 +1,248 @@
/*******************************************************************************
copyright: Copyright (c) 2006 Juan Jose Comellas. All rights reserved
license: BSD style: $(LICENSE)
author: Juan Jose Comellas <juanjo@comellas.com.ar>
Converted to use core.sync by Sean Kelly <sean@f4.ca>
*******************************************************************************/
private import tango.core.sync.Mutex;
private import tango.core.Exception;
private import tango.core.Thread;
private import tango.io.Stdout;
private import tango.text.convert.Integer;
debug (mutex)
{
private import tango.util.log.Log;
private import tango.util.log.ConsoleAppender;
private import tango.util.log.DateLayout;
}
/**
* Example program for the tango.core.sync.Mutex module.
*/
void main(char[][] args)
{
debug (mutex)
{
Logger log = Log.getLogger("mutex");
log.addAppender(new ConsoleAppender(new DateLayout()));
log.info("Mutex test");
}
testNonRecursive();
testLocking();
testRecursive();
}
/**
* Test that non-recursive mutexes actually do what they're supposed to do.
*
* Remarks:
* Windows only supports recursive mutexes.
*/
void testNonRecursive()
{
version (Posix)
{
debug (mutex)
{
Logger log = Log.getLogger("mutex.non-recursive");
}
Mutex mutex = new Mutex(Mutex.Type.NonRecursive);
bool couldLock;
try
{
mutex.lock();
debug (mutex)
log.trace("Acquired mutex");
couldLock = mutex.tryLock();
if (couldLock)
{
debug (mutex)
{
log.trace("Re-acquired mutex");
log.trace("Releasing mutex");
}
mutex.unlock();
}
else
{
debug (mutex)
log.trace("Re-acquiring the mutex failed");
}
debug (mutex)
log.trace("Releasing mutex");
mutex.unlock();
}
catch (SyncException e)
{
Stderr.formatln("Sync exception caught when testing non-recursive mutexes:\n{0}\n", e.toString());
}
catch (Exception e)
{
Stderr.formatln("Unexpected exception caught when testing non-recursive mutexes:\n{0}\n", e.toString());
}
if (!couldLock)
{
debug (mutex)
log.info("The non-recursive Mutex test was successful");
}
else
{
debug (mutex)
{
log.error("Non-recursive mutexes are not working: "
"Mutex.tryAcquire() did not fail on an already acquired mutex");
assert(false);
}
else
{
assert(false, "Non-recursive mutexes are not working: "
"Mutex.tryAcquire() did not fail on an already acquired mutex");
}
}
}
}
/**
* Create several threads that acquire and release a mutex several times.
*/
void testLocking()
{
const uint MaxThreadCount = 10;
const uint LoopsPerThread = 1000;
debug (mutex)
{
Logger log = Log.getLogger("mutex.locking");
}
Mutex mutex = new Mutex();
uint lockCount = 0;
void mutexLockingThread()
{
try
{
for (uint i; i < LoopsPerThread; i++)
{
synchronized (mutex)
{
lockCount++;
}
}
}
catch (SyncException e)
{
Stderr.formatln("Sync exception caught inside mutex testing thread:\n{0}\n", e.toString());
}
catch (Exception e)
{
Stderr.formatln("Unexpected exception caught inside mutex testing thread:\n{0}\n", e.toString());
}
}
ThreadGroup group = new ThreadGroup();
Thread thread;
char[10] tmp;
for (uint i = 0; i < MaxThreadCount; i++)
{
thread = new Thread(&mutexLockingThread);
thread.name = "thread-" ~ format(tmp, i);
debug (mutex)
log.trace("Created thread " ~ thread.name);
thread.start();
group.add(thread);
}
debug (mutex)
log.trace("Waiting for threads to finish");
group.joinAll();
if (lockCount == MaxThreadCount * LoopsPerThread)
{
debug (mutex)
log.info("The Mutex locking test was successful");
}
else
{
debug (mutex)
{
log.error("Mutex locking is not working properly: "
"the number of times the mutex was acquired is incorrect");
assert(false);
}
else
{
assert(false,"Mutex locking is not working properly: "
"the number of times the mutex was acquired is incorrect");
}
}
}
/**
* Test that recursive mutexes actually do what they're supposed to do.
*/
void testRecursive()
{
const uint LoopsPerThread = 1000;
debug (mutex)
{
Logger log = Log.getLogger("mutex.recursive");
}
Mutex mutex = new Mutex;
uint lockCount = 0;
try
{
for (uint i = 0; i < LoopsPerThread; i++)
{
mutex.lock();
lockCount++;
}
}
catch (SyncException e)
{
Stderr.formatln("Sync exception caught in recursive mutex test:\n{0}\n", e.toString());
}
catch (Exception e)
{
Stderr.formatln("Unexpected exception caught in recursive mutex test:\n{0}\n", e.toString());
}
for (uint i = 0; i < lockCount; i++)
{
mutex.unlock();
}
if (lockCount == LoopsPerThread)
{
debug (mutex)
log.info("The recursive Mutex test was successful");
}
else
{
debug (mutex)
{
log.error("Recursive mutexes are not working: "
"the number of times the mutex was acquired is incorrect");
assert(false);
}
else
{
assert(false, "Recursive mutexes are not working: "
"the number of times the mutex was acquired is incorrect");
}
}
}

View File

@@ -0,0 +1,145 @@
/*******************************************************************************
copyright: Copyright (c) 2006 Juan Jose Comellas. All rights reserved
license: BSD style: $(LICENSE)
author: Juan Jose Comellas <juanjo@comellas.com.ar>
Converted to use core.sync by Sean Kelly <sean@f4.ca>
*******************************************************************************/
private import tango.core.sync.ReadWriteMutex;
private import tango.core.sync.Mutex;
private import tango.core.Thread;
private import tango.text.convert.Integer;
debug (readwritemutex)
{
private import tango.util.log.Log;
private import tango.util.log.ConsoleAppender;
private import tango.util.log.DateLayout;
}
/**
* Example program for the tango.core.sync.ReadWriteMutex module.
*/
void main(char[][] args)
{
const uint ReaderThreads = 100;
const uint WriterThreads = 20;
const uint LoopsPerReader = 10000;
const uint LoopsPerWriter = 1000;
const uint CounterIncrement = 3;
debug (readwritemutex)
{
Logger log = Log.getLogger("readwritemutex");
log.addAppender(new ConsoleAppender(new DateLayout()));
log.info("ReadWriteMutex test");
}
ReadWriteMutex rwlock = new ReadWriteMutex();
Mutex mutex = new Mutex();
uint readCount = 0;
uint passed = 0;
uint failed = 0;
void mutexReaderThread()
{
debug (readwritemutex)
{
Logger log = Log.getLogger("readwritemutex." ~ Thread.getThis().name());
log.trace("Starting reader thread");
}
for (uint i = 0; i < LoopsPerReader; ++i)
{
// All the reader threads acquire the mutex for reading and when they are
// all done
synchronized (rwlock.reader)
{
for (uint j = 0; j < CounterIncrement; ++j)
{
synchronized (mutex)
{
++readCount;
}
}
}
}
}
void mutexWriterThread()
{
debug (readwritemutex)
{
Logger log = Log.getLogger("readwritemutex." ~ Thread.getThis().name());
log.trace("Starting writer thread");
}
for (uint i = 0; i < LoopsPerWriter; ++i)
{
synchronized (rwlock.writer)
{
synchronized (mutex)
{
if (readCount % 3 == 0)
{
++passed;
}
}
}
}
}
ThreadGroup group = new ThreadGroup();
Thread thread;
char[10] tmp;
for (uint i = 0; i < ReaderThreads; ++i)
{
thread = new Thread(&mutexReaderThread);
thread.name = "reader-" ~ format(tmp, i);
debug (readwritemutex)
log.trace("Created reader thread " ~ thread.name);
thread.start();
group.add(thread);
}
for (uint i = 0; i < WriterThreads; ++i)
{
thread = new Thread(&mutexWriterThread);
thread.name = "writer-" ~ format(tmp, i);
debug (readwritemutex)
log.trace("Created writer thread " ~ thread.name);
thread.start();
group.add(thread);
}
debug (readwritemutex)
log.trace("Waiting for threads to finish");
group.joinAll();
if (passed == WriterThreads * LoopsPerWriter)
{
debug (readwritemutex)
log.info("The ReadWriteMutex test was successful");
}
else
{
debug (readwritemutex)
{
log.error("The ReadWriteMutex is not working properly: the counter has an incorrect value");
assert(false);
}
else
{
assert(false, "The ReadWriteMutex is not working properly: the counter has an incorrect value");
}
}
}

View File

@@ -0,0 +1,340 @@
/*******************************************************************************
copyright: Copyright (c) 2007 Juan Jose Comellas. All rights reserved
license: BSD style: $(LICENSE)
author: Juan Jose Comellas <juanjo@comellas.com.ar>
Converted to use core.sync by Sean Kelly <sean@f4.ca>
*******************************************************************************/
module semaphore;
private import tango.core.sync.Semaphore;
private import tango.core.sync.Mutex;
private import tango.core.Exception;
private import tango.core.Exception;
private import tango.core.Thread;
private import tango.io.Console;
private import tango.text.stream.LineIterator;
private import tango.text.convert.Integer;
private import tango.sys.Process;
debug (semaphore)
{
private import tango.util.log.Log;
private import tango.util.log.ConsoleAppender;
private import tango.util.log.DateLayout;
}
const char[] SemaphoreName = "TestProcessSemaphore";
/**
* Example program for the tango.core.sync.Barrier module.
*/
int main(char[][] args)
{
if (args.length == 1)
{
debug (semaphore)
{
Logger log = Log.getLogger("semaphore");
log.addAppender(new ConsoleAppender(new DateLayout()));
log.info("Semaphore test");
}
testSemaphore();
testProcessSemaphore(args[0]);
return 0;
}
else
{
return testSecondProcessSemaphore();
}
}
/**
* Test for single-process (multi-threaded) semaphores.
*/
void testSemaphore()
{
const uint MaxThreadCount = 10;
// Semaphore used in the tests. Start it "locked" (i.e., its initial
// count is 0).
Semaphore sem = new Semaphore(MaxThreadCount - 1);
Mutex mutex = new Mutex();
uint count = 0;
bool passed = false;
void semaphoreTestThread()
{
debug (semaphore)
{
Logger log = Log.getLogger("semaphore.single." ~ Thread.getThis().name());
log.trace("Starting thread");
}
try
{
uint threadNumber;
// 'count' is a resource shared by multiple threads, so we must
// acquire the mutex before modifying it.
synchronized (mutex)
{
// debug (semaphore)
// log.trace("Acquired mutex");
threadNumber = ++count;
// debug (semaphore)
// log.trace("Releasing mutex");
}
// We wait for all the threads to finish counting.
if (threadNumber < MaxThreadCount)
{
sem.wait();
debug (semaphore)
log.trace("Acquired semaphore");
while (true)
{
synchronized (mutex)
{
if (count >= MaxThreadCount + 1)
break;
}
Thread.yield();
}
debug (semaphore)
log.trace("Releasing semaphore");
sem.notify();
}
else
{
passed = !sem.tryWait();
if (passed)
{
debug (semaphore)
log.trace("Tried to acquire the semaphore too many times and failed: OK");
}
else
{
debug (semaphore)
log.error("Tried to acquire the semaphore too may times and succeeded: FAILED");
debug (semaphore)
log.trace("Releasing semaphore");
sem.notify();
}
synchronized (mutex)
{
count++;
}
}
}
catch (SyncException e)
{
Cerr("Sync exception caught in Semaphore test thread " ~ Thread.getThis().name ~
":\n" ~ e.toString()).newline;
}
catch (Exception e)
{
Cerr("Unexpected exception caught in Semaphore test thread " ~ Thread.getThis().name ~
":\n" ~ e.toString()).newline;
}
}
debug (semaphore)
{
Logger log = Log.getLogger("semaphore.single");
}
ThreadGroup group = new ThreadGroup();
Thread thread;
char[10] tmp;
for (uint i = 0; i < MaxThreadCount; ++i)
{
thread = new Thread(&semaphoreTestThread);
thread.name = "thread-" ~ tango.text.convert.Integer.format(tmp, i);
group.add(thread);
debug (semaphore)
log.trace("Created thread " ~ thread.name);
thread.start();
}
debug (semaphore)
log.trace("Waiting for threads to finish");
group.joinAll();
if (passed)
{
debug (semaphore)
log.info("The Semaphore test was successful");
}
else
{
debug (semaphore)
{
log.error("The Semaphore is not working properly: it allowed "
"to be acquired more than it should have done");
assert(false);
}
else
{
assert(false, "The Semaphore is not working properly: it allowed "
"to be acquired more than it should have done");
}
}
}
/**
* Test for multi-process semaphores: this test works by creating a copy of
* this process that tries to acquire the ProcessSemaphore that was created
* in this function. If everything works as expected, the attempt should fail,
* as the count of the semaphore is set to 1.
*/
void testProcessSemaphore(char[] programName)
{
/+
bool success = false;
debug (semaphore)
{
Logger log = Log.getLogger("semaphore.multi");
Logger childLog = Log.getLogger("semaphore.multi.child");
log.info("ProcessSemaphore test");
}
try
{
scope ProcessSemaphore sem = new ProcessSemaphore(SemaphoreName, 1);
Process proc = new Process(programName, "2");
debug (semaphore)
log.trace("Created ProcessSemaphore('" ~ SemaphoreName ~ "')'");
sem.wait();
debug (semaphore)
log.trace("Acquired semaphore in main process");
debug (semaphore)
log.trace("Executing child test process: " ~ proc.toString());
proc.execute();
debug (semaphore)
{
foreach (line; new LineIterator!(char)(proc.stdout))
{
childLog.trace(line);
}
}
foreach (line; new LineIterator!(char)(proc.stderr))
{
Cerr(line).newline;
}
debug (semaphore)
log.trace("Waiting for child process to finish");
auto result = proc.wait();
success = (result.reason == Process.Result.Exit && result.status == 2);
debug (semaphore)
log.trace("Releasing semaphore in main process");
sem.notify();
}
catch (SyncException e)
{
Cerr("Sync exception caught in ProcessSemaphore main test process:\n" ~ e.toString()).newline;
}
catch (ProcessException e)
{
Cerr("Process exception caught in ProcessSemaphore main test process:\n" ~ e.toString()).newline;
}
catch (Exception e)
{
Cerr("Unexpected exception caught in ProcessSemaphore main test process:\n" ~ e.toString()).newline;
}
if (success)
{
debug (semaphore)
log.info("The ProcessSemaphore test was successful");
}
else
{
debug (semaphore)
{
log.error("The multi-process semaphore is not working");
assert(false);
}
else
{
assert(false, "The multi-process semaphore is not working");
}
}
+/
}
/**
* Test for multi-process semaphores (second process).
*/
int testSecondProcessSemaphore()
{
int rc = 0;
/+
debug (semaphore)
{
Cout("Starting child process\n");
}
try
{
scope ProcessSemaphore sem = new ProcessSemaphore(SemaphoreName);
bool success;
success = !sem.tryAcquire();
if (success)
{
debug (semaphore)
Cout("Tried to acquire semaphore in child process and failed: OK\n");
rc = 2;
}
else
{
debug (semaphore)
{
Cout("Acquired semaphore in child process: this should not have happened\n");
Cout("Releasing semaphore in child process\n");
}
sem.notify();
rc = 1;
}
}
catch (SyncException e)
{
Cerr("Sync exception caught in ProcessSemaphore child test process:\n" ~ e.toString()).newline;
}
catch (ProcessException e)
{
Cerr("Process exception caught in ProcessSemaphore child test process:\n" ~ e.toString()).newline;
}
catch (Exception e)
{
Cerr("Unexpected exception caught in ProcessSemaphore child test process:\n" ~ e.toString()).newline;
}
debug (semaphore)
Cout("Leaving child process\n");
+/
return rc;
}

View File

@@ -0,0 +1,97 @@
/*******************************************************************************
Illustrates use of the Arguments class.
*******************************************************************************/
import tango.util.Arguments;
import tango.io.Stdout;
import tango.io.FileConduit;
import tango.text.stream.LineIterator;
void usage()
{
Stdout("Usage: [OPTIONS]... FILES...").newline;
Stdout("This is a program that does something.").newline;
Stdout.newline;
Stdout("OPTIONS: ").newline;
Stdout("Output this help message: -?, --help").newline;
Stdout("Do cool things to your files: -c, -C, --cool").newline;
Stdout("Use filename as response file: -r, -R, --response").newline;
}
void main(char[][] cmdlArgs)
{
char[][] implicitArguments = ["files"];
char[][][] argumentAliases;
argumentAliases ~= ["help", "?"];
argumentAliases ~= ["cool", "C", "c"];
argumentAliases ~= ["response", "R", "r"];
auto args = new Arguments(cmdlArgs, implicitArguments, argumentAliases);
bool fileExistsValidation(char[] arg)
{
bool rtn;
FilePath argFile = new FilePath(arg);
rtn = argFile.exists;
if (!rtn)
Stdout.format("Specified path does not exist: {}", arg).newline;
return rtn;
}
bool singleFileValidation(char[][] args, inout char[] invalidArg)
{
if (args.length > 1)
{
Stdout("Cannot specify multiple paths for argument.").newline;
invalidArg = args[1];
}
else
return true;
return false;
}
args.addValidation("response", &fileExistsValidation);
args.addValidation("response", &singleFileValidation);
args.addValidation("files", true, true);
bool argsValidated = true;
try
args.validate;
catch (ArgumentException ex)
{
if (ex.reason == ArgumentException.ExceptionReason.MISSING_ARGUMENT)
Stdout.format("Missing Argument: {} ({})", ex.name, ex.msg).newline;
else if (ex.reason == ArgumentException.ExceptionReason.MISSING_PARAMETER)
Stdout.format("Missing Parameter to Argument: {} ({})", ex.name, ex.msg).newline;
else if (ex.reason == ArgumentException.ExceptionReason.INVALID_PARAMETER)
Stdout.format("Invalid Parameter: {} ({})", ex.name, ex.msg).newline;
Stdout.newline;
argsValidated = false;
}
if ((!argsValidated) || ("help" in args))
usage();
else
{// ready to run
if ("response" in args)
{
Stdout(args["response"][0]).newline;
auto file = new FileConduit(args["response"][0]);
auto lines = new LineIterator!(char)(file);
char[][] arguments;
foreach (line; lines)
arguments ~= line.dup;
args.parse(arguments, implicitArguments, argumentAliases);
}
if ("cool" in args)
{
Stdout ("Listing the files to be actioned in a cool way.").newline;
foreach (char[] file; args["files"])
Stdout.format("{}", file).newline;
Stdout ("Cool and secret action performed.").newline;
}
if ("x" in args)
Stdout.format("User set the X factor to '{}'", args["x"]).newline;
}
}

View File

@@ -0,0 +1,54 @@
/*******************************************************************************
localtime.d
*******************************************************************************/
private import tango.io.Stdout;
private import tango.time.WallClock;
/******************************************************************************
Example code to format a local time in the following format:
"Wed Dec 31 16:00:00 GMT-0800 1969". The day and month names
would typically be extracted from a locale instance, but we
convert them locally here for the sake of simplicity
******************************************************************************/
void main ()
{
/// list of day names
static char[][] days = ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"];
/// list of month names
static char[][] months =
[
"Jan", "Feb", "Mar", "Apr", "May", "Jun",
"Jul", "Aug", "Sep", "Oct", "Nov", "Dec",
];
// retreive local time
auto dt = WallClock.toDate;
// get GMT difference in minutes
auto tz = cast(int) WallClock.zone.minutes;
char sign = '+';
if (tz < 0)
tz = -tz, sign = '-';
// format date
Stdout.formatln ("{}, {} {:d2} {:d2}:{:d2}:{:d2} GMT{}{:d2}:{:d2} {}",
days[dt.date.dow],
months[dt.date.month-1],
dt.date.day,
dt.time.hours,
dt.time.minutes,
dt.time.seconds,
sign,
tz / 60,
tz % 60,
dt.date.year
);
}

View File

@@ -0,0 +1,51 @@
/*****************************************************************
Simple example that shows possible inputs to normalize and the
corresponding outputs.
Put into public domain by Lars Ivar Igesund.
*****************************************************************/
import tango.io.Stdout;
import tango.util.PathUtil;
int main()
{
version (Posix) {
Stdout(normalize ( "/foo/../john")).newline;
Stdout(normalize ( "foo/../john")).newline;
Stdout(normalize ( "foo/bar/..")).newline;
Stdout(normalize ( "foo/bar/../john")).newline;
Stdout(normalize ( "foo/bar/doe/../../john")).newline;
Stdout(normalize ( "foo/bar/doe/../../john/../bar")).newline;
Stdout(normalize ( "./foo/bar/doe")).newline;
Stdout(normalize ( "./foo/bar/doe/../../john/../bar")).newline;
Stdout(normalize ( "./foo/bar/../../john/../bar")).newline;
Stdout(normalize ( "foo/bar/./doe/../../john")).newline;
Stdout(normalize ( "../../foo/bar/./doe/../../john")).newline;
Stdout(normalize ( "../../../foo/bar")).newline;
Stdout("** Should now throw exception as the following path is invalid for normalization.").newline;
Stdout(normalize ( "/../../../foo/bar")).newline;
}
version (Windows) {
Stdout(normalize ( "C:\\foo\\..\\john")).newline;
Stdout(normalize ( "foo\\..\\john")).newline;
Stdout(normalize ( "foo\\bar\\..")).newline;
Stdout(normalize ( "foo\\bar\\..\\john")).newline;
Stdout(normalize ( "foo\\bar\\doe\\..\\..\\john")).newline;
Stdout(normalize ( "foo\\bar\\doe\\..\\..\\john\\..\\bar")).newline;
Stdout(normalize ( ".\\foo\\bar\\doe")).newline;
Stdout(normalize ( ".\\foo\\bar\\doe\\..\\..\\john\\..\\bar")).newline;
Stdout(normalize ( ".\\foo\\bar\\..\\..\\john\\..\\bar")).newline;
Stdout(normalize ( "foo\\bar\\.\\doe\\..\\..\\john")).newline;
Stdout(normalize ( "..\\..\\foo\\bar\\.\\doe\\..\\..\\john")).newline;
Stdout(normalize ( "..\\..\\..\\foo\\bar")).newline;
Stdout("** Should now throw exception as the following path is invalid for normalization.").newline;
Stdout(normalize ( "C:\\..\\..\\..\\foo\\bar")).newline;
}
return 0;
}

View File

@@ -0,0 +1,60 @@
/*******************************************************************************
copyright: Copyright (c) 2006 Juan Jose Comellas. All rights reserved
license: BSD style: $(LICENSE)
author: Juan Jose Comellas <juanjo@comellas.com.ar>
*******************************************************************************/
private import tango.io.Stdout;
private import tango.sys.Process;
private import tango.core.Exception;
private import tango.text.stream.LineIterator;
/**
* Example program for the tango.sys.Process class.
*/
void main()
{
version (Windows)
char[] command = "ping -n 4 localhost";
else version (Posix)
char[] command = "ping -c 4 localhost";
else
assert(false, "Unsupported platform");
try
{
auto p = new Process(command, null);
Stdout.formatln("Executing {0}", p.toString());
p.execute();
Stdout.formatln("Output from process: {0} (pid {1})\n---",
p.programName, p.pid);
foreach (line; new LineIterator!(char)(p.stdout))
{
Stdout.formatln("{0}", line);
}
Stdout.print("---\n");
auto result = p.wait();
Stdout.formatln("Process '{0}' ({1}) finished: {2}",
p.programName, p.pid, result.toString());
}
catch (ProcessException e)
{
Stdout.formatln("Process execution failed: {0}", e.toString());
}
catch (IOException e)
{
Stdout.formatln("Input/output exception caught: {0}", e.toString());
}
catch (Exception e)
{
Stdout.formatln("Unexpected exception caught: {0}", e.toString());
}
}

View File

@@ -0,0 +1,20 @@
/**
Example showing how the alignment component in a format string argument works.
Put into public domain by Lars Ivar Igesund
*/
import tango.io.Stdout;
void main(){
char[] myFName = "Johnny";
Stdout.formatln("First Name = |{0,15}|", myFName);
Stdout.formatln("Last Name = |{0,15}|", "Foo de Bar");
Stdout.formatln("First Name = |{0,-15}|", myFName);
Stdout.formatln("Last Name = |{0,-15}|", "Foo de Bar");
Stdout.formatln("First name = |{0,5}|", myFName);
}

View File

@@ -0,0 +1,15 @@
/**
Example that shows how the format specifiers can be used to index into
the argument list.
Put into public domain by Lars Ivar Igesund.
*/
import tango.io.Stdout;
void main(){
Stdout.formatln("Many {1} can now be {0} around to make {2} easier,\n and {1} can also be repeated.",
"switched", "arguments", "localization");
}

View File

@@ -0,0 +1,17 @@
/**
Example showing how to use format specifier components in a format string's
argument.
Put into public domain by Lars Ivar Igesund
*/
import tango.io.Stdout;
void main(){
double avogadros = 6.0221415e23;
Stdout.formatln("I have {0:C} in cash.", 100);
Stdout.formatln("Avogadro's number is {0:E}.", avogadros);
Stdout.formatln("Avogadro's number (with alignment) is {0,4:E}.", avogadros);
}

View File

@@ -0,0 +1,21 @@
/******************************************************************************
Example to format a locale-based time. For a default locale of
en-gb, this examples formats in the following manner:
"Thu, 27 April 2006 18:20:47 +1"
******************************************************************************/
private import tango.io.Console;
private import tango.time.Clock;
private import tango.text.locale.Locale;
void main ()
{
auto layout = new Locale;
Cout (layout ("{:ddd, dd MMMM yyyy HH:mm:ss z}", Clock.now)).newline;
}

View File

@@ -0,0 +1,32 @@
private import tango.io.Buffer,
tango.io.Console;
private import tango.text.Properties;
/*******************************************************************************
Illustrates simple usage of tango.text.Properties
*******************************************************************************/
void main()
{
char[][char[]] aa;
aa ["foo"] = "something";
aa ["bar"] = "something else";
aa ["wumpus"] = "";
// write associative-array to a buffer; could use a file
auto props = new Properties!(char);
auto buffer = new Buffer (256);
props.save (buffer, aa);
// reset and repopulate AA from the buffer
aa = null;
props.load (buffer, (char[] name, char[] value){aa[name] = value;});
// display result
foreach (name, value; aa)
Cout (name) (" = ") (value).newline;
}

View File

@@ -0,0 +1,25 @@
/*******************************************************************************
Tokenize input from the console. There are a variety of handy
tokenizers in the tango.text package ~ this illustrates usage
of an iterator that recognizes quoted-strings within an input
array, and splits elements on a provided set of delimiters
*******************************************************************************/
import tango.io.Console;
import Text = tango.text.Util;
void main()
{
// flush the console output, since we have no newline present
Cout ("Please enter some space-separated tokens: ") ();
// create quote-aware iterator for handling space-delimited
// tokens from the console input
foreach (element; Text.quotes (Text.trim(Cin.get), " \t"))
Cout ("<") (element) ("> ");
Cout.newline;
}