mirror of
https://github.com/xomboverlord/ldc.git
synced 2026-07-21 22:55:23 +02:00
[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:
60
tango/example/cluster/Add.d
Normal file
60
tango/example/cluster/Add.d
Normal 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);}
|
||||
}
|
||||
36
tango/example/cluster/alert.d
Normal file
36
tango/example/cluster/alert.d
Normal 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);
|
||||
}
|
||||
48
tango/example/cluster/cclient.d
Normal file
48
tango/example/cluster/cclient.d
Normal 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 ...");
|
||||
}
|
||||
|
||||
30
tango/example/cluster/cserver.d
Normal file
30
tango/example/cluster/cserver.d
Normal 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;
|
||||
}
|
||||
38
tango/example/cluster/invalidate.d
Normal file
38
tango/example/cluster/invalidate.d
Normal 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);
|
||||
}
|
||||
44
tango/example/cluster/qclient.d
Normal file
44
tango/example/cluster/qclient.d
Normal 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);
|
||||
}
|
||||
}
|
||||
|
||||
41
tango/example/cluster/qlisten.d
Normal file
41
tango/example/cluster/qlisten.d
Normal 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);
|
||||
}
|
||||
30
tango/example/cluster/qrequest.d
Normal file
30
tango/example/cluster/qrequest.d
Normal 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");
|
||||
}
|
||||
27
tango/example/cluster/qserver.d
Normal file
27
tango/example/cluster/qserver.d
Normal 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;
|
||||
}
|
||||
38
tango/example/cluster/reply.d
Normal file
38
tango/example/cluster/reply.d
Normal 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);
|
||||
}
|
||||
15
tango/example/cluster/task.d
Normal file
15
tango/example/cluster/task.d
Normal 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));
|
||||
}
|
||||
|
||||
42
tango/example/cluster/tclient.d
Normal file
42
tango/example/cluster/tclient.d
Normal 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);
|
||||
}
|
||||
}
|
||||
|
||||
34
tango/example/cluster/tserver.d
Normal file
34
tango/example/cluster/tserver.d
Normal 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;
|
||||
}
|
||||
}
|
||||
806
tango/example/concurrency/fiber_test.d
Normal file
806
tango/example/concurrency/fiber_test.d
Normal 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");
|
||||
}
|
||||
|
||||
319
tango/example/conduits/FileBucket.d
Normal file
319
tango/example/conduits/FileBucket.d
Normal 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 ();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
85
tango/example/conduits/composite.d
Normal file
85
tango/example/conduits/composite.d
Normal 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);
|
||||
}
|
||||
|
||||
30
tango/example/conduits/filebubbler.d
Normal file
30
tango/example/conduits/filebubbler.d
Normal 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;
|
||||
}
|
||||
|
||||
26
tango/example/conduits/filecat.d
Normal file
26
tango/example/conduits/filecat.d
Normal 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");
|
||||
}
|
||||
23
tango/example/conduits/filecopy.d
Normal file
23
tango/example/conduits/filecopy.d
Normal 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;
|
||||
}
|
||||
26
tango/example/conduits/fileops.d
Normal file
26
tango/example/conduits/fileops.d
Normal 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);
|
||||
}
|
||||
11
tango/example/conduits/filepathname.d
Normal file
11
tango/example/conduits/filepathname.d
Normal 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;
|
||||
}
|
||||
32
tango/example/conduits/filescan.d
Normal file
32
tango/example/conduits/filescan.d
Normal 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;
|
||||
}
|
||||
40
tango/example/conduits/filescanregex.d
Normal file
40
tango/example/conduits/filescanregex.d
Normal 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;
|
||||
}
|
||||
|
||||
29
tango/example/conduits/lineio.d
Normal file
29
tango/example/conduits/lineio.d
Normal 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;
|
||||
}
|
||||
24
tango/example/conduits/mmap.d
Normal file
24
tango/example/conduits/mmap.d
Normal 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;
|
||||
}
|
||||
13
tango/example/conduits/paths.d
Normal file
13
tango/example/conduits/paths.d
Normal 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);
|
||||
}
|
||||
38
tango/example/conduits/randomio.d
Normal file
38
tango/example/conduits/randomio.d
Normal 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();
|
||||
}
|
||||
25
tango/example/conduits/unifile.d
Normal file
25
tango/example/conduits/unifile.d
Normal 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;
|
||||
}
|
||||
17
tango/example/console/buffered.d
Normal file
17
tango/example/console/buffered.d
Normal 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;
|
||||
}
|
||||
21
tango/example/console/hello.d
Normal file
21
tango/example/console/hello.d
Normal 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;
|
||||
}
|
||||
14
tango/example/console/stdout.d
Normal file
14
tango/example/console/stdout.d
Normal 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
35
tango/example/dsss.conf
Normal 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
42
tango/example/external/GlueFlectioned.d
vendored
Normal 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 );
|
||||
}
|
||||
76
tango/example/jake-all.bat
Normal file
76
tango/example/jake-all.bat
Normal 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
89
tango/example/linux.mak
Normal 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
|
||||
|
||||
|
||||
|
||||
|
||||
30
tango/example/logging/chainsaw.d
Normal file
30
tango/example/logging/chainsaw.d
Normal 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);
|
||||
}
|
||||
}
|
||||
322
tango/example/logging/context.d
Normal file
322
tango/example/logging/context.d
Normal 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");
|
||||
}
|
||||
}
|
||||
|
||||
102
tango/example/logging/logging.d
Normal file
102
tango/example/logging/logging.d
Normal 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);
|
||||
}
|
||||
}
|
||||
31
tango/example/logging/multilog.d
Normal file
31
tango/example/logging/multilog.d
Normal 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");
|
||||
}
|
||||
|
||||
237
tango/example/manual/chapterStorage.d
Normal file
237
tango/example/manual/chapterStorage.d
Normal 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;
|
||||
}
|
||||
|
||||
|
||||
30
tango/example/networking/homepage.d
Normal file
30
tango/example/networking/homepage.d
Normal 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;
|
||||
}
|
||||
27
tango/example/networking/httpget.d
Normal file
27
tango/example/networking/httpget.d
Normal 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) ();
|
||||
}
|
||||
|
||||
399
tango/example/networking/selector.d
Normal file
399
tango/example/networking/selector.d
Normal 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;
|
||||
}
|
||||
29
tango/example/networking/sockethello.d
Normal file
29
tango/example/networking/sockethello.d
Normal 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;
|
||||
}
|
||||
53
tango/example/networking/socketserver.d
Normal file
53
tango/example/networking/socketserver.d
Normal 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;
|
||||
}
|
||||
138
tango/example/synchronization/barrier.d
Normal file
138
tango/example/synchronization/barrier.d
Normal 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");
|
||||
}
|
||||
}
|
||||
}
|
||||
307
tango/example/synchronization/condition.d
Normal file
307
tango/example/synchronization/condition.d
Normal 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(¬ifyOneTestThread);
|
||||
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(¬ifyAllTestThread);
|
||||
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());
|
||||
}
|
||||
}
|
||||
248
tango/example/synchronization/mutex.d
Normal file
248
tango/example/synchronization/mutex.d
Normal 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");
|
||||
}
|
||||
}
|
||||
}
|
||||
145
tango/example/synchronization/readwritemutex.d
Normal file
145
tango/example/synchronization/readwritemutex.d
Normal 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");
|
||||
}
|
||||
}
|
||||
}
|
||||
340
tango/example/synchronization/semaphore.d
Normal file
340
tango/example/synchronization/semaphore.d
Normal 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;
|
||||
}
|
||||
97
tango/example/system/arguments.d
Normal file
97
tango/example/system/arguments.d
Normal 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;
|
||||
}
|
||||
}
|
||||
54
tango/example/system/localtime.d
Normal file
54
tango/example/system/localtime.d
Normal 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
|
||||
);
|
||||
}
|
||||
51
tango/example/system/normpath.d
Normal file
51
tango/example/system/normpath.d
Normal 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;
|
||||
}
|
||||
60
tango/example/system/process.d
Normal file
60
tango/example/system/process.d
Normal 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());
|
||||
}
|
||||
}
|
||||
20
tango/example/text/formatalign.d
Normal file
20
tango/example/text/formatalign.d
Normal 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);
|
||||
}
|
||||
15
tango/example/text/formatindex.d
Normal file
15
tango/example/text/formatindex.d
Normal 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");
|
||||
}
|
||||
17
tango/example/text/formatspec.d
Normal file
17
tango/example/text/formatspec.d
Normal 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);
|
||||
}
|
||||
21
tango/example/text/localetime.d
Normal file
21
tango/example/text/localetime.d
Normal 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;
|
||||
}
|
||||
32
tango/example/text/properties.d
Normal file
32
tango/example/text/properties.d
Normal 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;
|
||||
}
|
||||
|
||||
25
tango/example/text/token.d
Normal file
25
tango/example/text/token.d
Normal 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;
|
||||
}
|
||||
Reference in New Issue
Block a user