 Okay, so our next speaker is once again myself, Jean-Pierre Rosin. Okay, it works. About distributed programming. So this was announced as a live demo, which is always frightening, because you never know if it would work. So I'll try to go relatively fast on the slides, then we'll make the demo and if we have some spare time, where we can show you another example, or maybe we can just come back to some of what I said. So this is defined in the reference manual in an annex, in the distributed systems annex. So it means that it's not required from every compiler to implement that. In practice, only Gnat did implement that. And I'm a bit sorry about that, because it's a real nice feature of the ADA, and it would be nice if we had other compilers to implement it. So to understand what it means, it's one, it's about having one program distributed over several machines. You know, distributed systems that work, well, with several in general executions concurrently, may have different levels of coupling. You have tasking when it's inside a single program with shared memory. Then you have the distributed systems annex, where it is a single program which is split over several computers. And then you may have different programs communicating over the network. So here it's a single program. So we'll see it keeps all strong typing and all check that you can have, but still running on several machines. And the goal of the annex is to make the programmer concentrate on the problem and be freed from all the nasty details of networking, communication, differences in representation, and all those programs that play distributed programs. So an ADA program is made of several partitions. So it's a very abstract definition, but to be honest, it's several executables. You have several executables, you run them together and together they build the whole program. You have active partition that can run any program, also passive partition that can contain code and data, but no task, nothing that's active. You can even have several main programs. The language requires only at least one main program in one of the partitions, but you may have several main programs including one initial partition. So this maps to different patterns. For example, if you imagine a hardware board plugged in a backplane, so an active partition will be a processor board with a CPU and whatever, but a passive partition will be a memory board. You can have RAM for variables, RAM where you have the code of your sub-programs, but nothing that can run directly on the board. On a local area network, while it's a classical stuff with computers, you may wonder how you could make passive partitions. Actually, Gnat manages to be able to have passive partitions. Its variable is modeled as a file over NFS. Not very efficient, but it works. And it provides you with persistence by the same token, so it can be quite interesting if you don't abuse it. And even at the level of a single operating system, while you could view the active partition as the process level, tasking is about the threads level and that would be about the process level. For example, a set of sub-programs that is shared between several executables that would map to a dialog. So I'm not saying that the current Gnat implementation does all that, but I want just to stress that the model is not linked to a particular model. It has always been intended to be implementable in several contexts. What I mean is if you take, for example, Java's remote method in vocation, you designate a remote object through its IP address. So it's purely Internet that nothing else you can do with it. Here it has been carefully made independent of any particular network. So of course, not all data can circulate on a network. A task, a pointer would be hard to, if you exchange them, that would not be possible or it would be meaningless. So you need to have some restrictions. So the idea is you have a number of aspects that define how a package will behave regarding distribution. I won't go into too many details, but just to mention a regular package that has no categorization, these are called categorization aspects. Without categorization, the packages are duplicated, so they are independent. If you have a variable in a package in a partition, that variable is not related to the same variable, while with the same name in another partition. Your shared passive package are used to manage the data that's directly shared. So it's a common memory between the different partitions. Remote types allow the definition of types that can be exchanged between the partitions. And remote call packages are the most interesting ones. The ones that can make RPCs, RCP remote procedure calls where you can, from a node, from a partition, call something that physically resides in another partition. And for each kind of categorization, of course, there are a number of restrictions about what you can do. Those restrictions are very complicated. You don't need to understand how it works. You put the categorization, you compile, and the compiler will tell you what's allowed and what's not allowed. In Ada, we trust the compiler, you know? And since all the consistency of the program is guaranteed at program level, this means that all the data representation are guaranteed to be consistent over all the partitions. So you get a remote call. When you call a subprogram that's something in another partition, directly by calling what is called a remote subprogram, subprogram with the aspect remote call interface, so it's a classic called RPC. Of course, you have to know the name of the package. If you call a subprogram, you have to know the name of the package. So in terms of distribution, it means you know the server. You have a certain special kind of pointer that allow you to designate a subprogram in another package. So it's a typical callback where you can call back a subprogram through a pointer in another partition. But then you have to get the pointer from somewhere, so you have to have some kind of name server that will provide you with the pointer to the subprogram being called. And you can also have remote objects, the famous remote access to class-wide types. So basically you can have a pointer to an object in the sense of a type, and you can invoke methods of objects that are located in a remote node. So you can have distributed objects and directly call the methods of the object. That's once again, since you need to first get the pointer to the object, then you need some name server. And the whole semantics of Ada is preserved. So if a remote call raises an exception, you'll get the exception. All typing are checks and to be consistent and so on. So distribution is completely transparent, at least for the client for the caller. When you transmit data over the network, the interface is designed in such a way that it chooses the stream's mechanism. In Ada we have streams inspired without any shame from C++ streams. And you have standard streams provided by the compiler and you can redefine the stream. So the marshaling operation transforming your high level data type into a stream of bytes is performed automatically by the compiler using the stream that you can redefine. So for example, if you have a type name, for example, password, you can redefine the marshaling function so that this type is always scripted. And that your own redefinition will be used by the compiler and therefore you are guaranteed that never to have a clear password over the network. So it means that strong typing is totally preserved. You don't send bytes, you send, you exchange through the normal mechanism high level data. So how would you write in general a distributed application? First of all, very important, you don't care about distribution. It's very tempting when you know that you'll have something that will work on several computers to think first, oh, what am I going to put on that computer and that. If you do that, any change will require a total redesign of your application and it will be extremely costly. The best way to design a distributed application is first to design your program on your own machine. It works and you debug it and you design it on a thing as a regular application. Then you categorize, you look at what you've done. You said, well, these are some casual utilities that don't mean anything special. Here I have a centralized database so it must be shared if it's ever distributed. Here are the types that are used to exchange data with a database and so on. So you just drop those little aspects on your code. You recompile and you borrow your head because in general there will be some restriction that's not obeyed. So you need to change things here or there to make sure that it corresponds to your intent. That's quite often, for example, you take off some type definition from a package to make a special remote types package or some changes like that. Then you decide on the distribution. You decide that you will make three partitions and, well, the only shared package are the remote call interface packages and of course the shared passives. So these are to be attached to some explicit partitions. This is done thanks to a configuration file and I'll give you an example shortly. You recompile. The compiler will care about everything and you will get as many executables as you have defined partitions and then it's up to you to start your partitions where you want, maybe on the same computer, maybe on a different computer. So that looks like a miracle. Are there some people who ever wrote distributed applications here? Okay, good. So you'll appreciate the demo. Okay, I'll turn now to this. Okay, it's a very simple program. And here I just have all the printing is done through that console package. So console just, can you read it or it's a bit too small? Okay, sorry. Okay, that's better. So it's just a package and we print by, for the moment, it's a regular package, no categorization. Okay. Then I have a server. This is a remote call interface package. You see there is a program, a remote call interface. And what it does, it registered clients. So when a client comes, it calls register, it keeps account of the number of registered clients. And when that count reaches the expected number, in that case three, then I have a function must terminate that tells us now everybody has arrived. So I can show you quickly the body if you want. And come on. Okay, this way. So you see I just have a global counter. But it's to show you that that counter must be shared between the different partitions, of course. And then the client at very simple also will fit just not. Okay, so the client just registers. It prints a message by printing message. Well, it keeps the time and why not must terminate? Why not everybody's there? It loops saying it waits for a certain number of seconds, delays one second. And when terminate must terminate becomes true just print that it's terminated. Okay. So for the moment. The interesting point is the console that prints the message for now it's a regular package. It will be duplicated in each partition. So I prepared everything so we don't waste time, but believe me, I didn't cheat. So if you want me to recompile in front of you, I'll be happy to do it. Okay, but you're not here to watch compilation time. So if I start partition one, it should start shorting. Oh, I forgot to mention the server itself, the server package prints when a new client arrives. So you have seen that. Can you see that? Excuse me. Sure, that's better. Okay, a bit small, but I think you can see. So it prints in its own window. I start the second partition, which is printing. You see the server print on the left and here it prints on its own window. Okay. If I start a third client, then everybody terminates. The count has been reached and everybody terminates. Okay. Now let's have some fun. I change to another directory where I prepared the second step of the demo. Okay. So now, oh, I forgot to show you something important. Sorry. The config, the configuration. So this is the file that defines the distribution, the configuration. Here you see I just declared three partitions. And I have only one remote call interface packet, the server. So I said that the partition one contains the server. Client is my main procedure. And well, in that system, there is a kind of main, main procedure, you know. So this one is in partition one. And there is a secondary main in partition two and partition three. That program has started here for them because I need to start all this in three different windows. I start them by hand. But you can have the compiler generate automatically a starter that will run every partition in, you can tell on which machine and so on. And the funny thing is that starter can be written either in Ada and automatically compiled or generated as a shell script. So if you want to see what happens. And that generated automatically. Okay, so we move to step two. What did I change here? Very few. I just added in my console pragma remote call interface. I didn't change anything else. But now in a sense I have a printer server instead of each partition printing in its own domain. I have a shared printer server and at the level of the configuration, you see I just added console since now it's a remote call interface package. It has to be assigned to a certain partition. So I assign it to partition one and the rest is the same. And then you recompile and I start partition one. There is a kind of middleware in behind if I have some time maybe I told you. For now it seems like before. But if now I start partition two. All messages for partition two are printed in the same window because that's the first window is where the printer server resides. Okay, and of course if I start my third partition, we'll see a new client and then everybody terminate. Okay, now imagine that I made some insufficient design about the load of my server and that first server is really too heavily loaded. Okay, that happens quite often on distributed systems. It's hard to foresee what the actual load will be. So I decide I'll put a fourth machine that will just hold the message server, the console. Fine, step three here. I didn't change anything in the era. I just changed the configuration file. Okay, for you to see. I define a fourth partition here where I put the console. Okay, that's all I did. I didn't change the software at all. Recompiled. Okay, it's here. So let's start partition one. Nothing happens. Of course it's printing in partition two, in partition four. Okay, let's start it now. And you see all the messages are now moving to that dedicated partition for printing. Of course, if I now start partition three up, everybody terminates nicely. So let's, this is just a small program to demonstrate it. But think about it. I first started with no centralized server. Each part of the program used its own local server. Then I decided to have one centralized server to do everything. I added one pragma, change the configuration file and recompile. That's all I had to do. And then I decided to change the way the load is split on my different server. I had a new machine with a new server. I changed one line in my configuration file. I didn't change my Ada software. And I had my new configuration. Okay. So generally people who never had to cope with distributed system find that very nice. Those who had to cope with distributed system are absolutely stunned by this because all the hard work. Yes, I have some spare minutes. I can show you something is done by the compiler. When you compile something with the distribution, you see here a little directory that appears with a name DSA. That means distributed systems architecture. Okay. And if you look at that, you will see the partitions. And you'll see all the files. You'll see a number of Ada files here that are all automatically generated by the systems and compiled by the system. That ensure the whole other data transmission, the coordination, finding the name server, and all those things that you need when you are doing a distributed system, all generated automatically. Okay. Fine. So, well, since we have someone started, so now I'll show you another example. How to make distributed objects. So it's not a live demo because it would be a bit long to do it. Imagine you have a tips. Well, nowadays, of course, you don't use tips anymore, but could be any server. Well, for archiving, well, when you see the amount of data you can put on micro SD card, it's very impressive. Anyway. So imagine I have one computer here. Yeah. Yeah. That manages tapes. Okay. So first I define an abstract data type tape with pure means it's another categorization package. That means that there is no side effect. And well, the basic operation rewind read and write. Okay. So if I want to access a distributed object, I mentioned that I need a name server. So on a different partition, I have that name server here. So it's a remote call interface. So you know where it is. And it defines a pointer to that tips.instance to class. So it's a class wide type. A class type that encloses all descendants. And since it's a regular name server, you register a pointer to a tape under a certain name. You can find from a name of the pointer, the corresponding pointer and you can unregister it if you want. Now you have an implementation of that object. So a tape driver. And so you define your own tape if you want the driver, the concrete implementation of a tape drive. And so you redefine the procedure rewind, read and write. By the way, the overriding keyword here makes sure that you are actually redefining a method and not due to some type or anything else. Defining something new. You declare two tape objects here and you register those tapes under a certain name to the name server. So you send to the remote name server a pointer that through the network designates your two tape drives. And then a client, which is a normal program without any categorization, can declare two objects of that pointer. It will ask the name server to give the pointer that through the network designate those tape drives and use that as usual. Now, maybe you didn't notice that, but there is nothing in that example that refers to distribution except some pragmas here or there. You just write it as a normal software and then you write your configuration to tell on which computer you want your tapes, on which computer you want the name server. And normally you build your program by calling netmake or gpr build on your project file. All you have to do is call pognetdist on the configuration file. And it will recompile everything and that's all you need. Okay, oh, I've been even a bit earlier. We have five minutes for questions. Wow, so impressed. Yes, please. Go ahead. Okay. Well, there is it, of course. Oh, yes. So the question was about the communication, the necessity of a firewall and problems with firewalls and so on. Of course, you need some middleware. And I was afraid of being a bit short in time so I didn't put the slide, but there is a middleware that's provided. I want to show you here in that window you have the middleware ready. Okay. And when the middleware starts, it gives you a magical string that somewhere here. Okay. And you have to set some environment variable to that magical string so that you have to boot from something from somewhere, of course. Now, of course, that middleware must be accessible from your partition. So, of course, if you have firewalls in the middle, actually, I intended to make the demo a bit more impressive by having one of the partitions running on my computer at Isilemolino near Paris. Okay. And through, of course, a VPN. But it happens that the Wi-Fi here, there is a firewall that does not let the VPNs. Okay. So I had to make it local. Okay. Normally, it would have been even better because it, well, trust me, I'm sorry. I'll come to my office in Isilemolino. I can show you that it's exactly the same thing if one of the partitions is running Linux since the TED of Windows. Okay. It works exactly the same. But, of course, you don't get rid of all the problems inherent with network. Okay. Another question. Okay. If it's not available when you start, you have an exception and the partition doesn't start. Okay. If you lose it, well, actually, the middle of... Maybe the ADACO people could answer that better. But the middleware allows you to exchange the IP addresses of the various partitions. I'm not sure that it plays a role once it's completely started. No. Okay. Well, it's a very specialized domain. So you have two or three guys at ADACO that know the answer and you just trust them. There is an exception that's intended for communication problems in the network interface. Yes, I can mention that now. All communications have to go through a dedicated package called system.rci. And that package and the implementation of that package is in charge of the transportation layer. And the standard makes it very explicit that you are allowed to provide your own implementation on that package. It means that if your compiler supports that annex and you have some proprietary networks, safety, critical, whatever, then you just have to rewrite the body of that package to handle all the communications. But the compiler does not know about the transportation layer. So you can use various networking solution with that. Okay. Something else, yeah? No. No, it's special to the Poly Orb implementation. Oh, yes, I was talking about that and I wondered somewhere else. But here the middleware I use is called Poly Orb. It's something that's provided by ADACO also. And it's called a schizophrenic middleware because it has several personalities in the same person at the same time. So it is a middleware for the distributed system annex. But at the same time, it's a Koba server and MME or something like that server and some other protocol. And it allows the simultaneous views of the same object. So you can declare a distributed object in Ada as a tag type. And from your C++ application, it will be viewed as a Koba class. Okay. So you can exchange data with different protocols and the middleware does a translation between the different views, different personalities of the middleware. Anything else? Okay. I think I'm writing time. Yeah. Okay. With pleasure. Now in Koba, you declare the types and it's intended for different applications that communicate through shared objects. And you hope that the ideal is the same on the various application. Ideal is the Koba language for defining the shared objects. But there is no cross checks, for example, that the various application use the same definition of your Koba object. Okay. In Ada, it's all compiled together with all the type controls that we are used to with Ada. So there is a guarantee that the data representation and the objects will be the same in all your partitions. But it's a single program split over several partitions. With Koba, it's more aware of having different programs that communicate through objects. There is a difference. Okay. Thank you for your attention.