From svg@REDACTED Sun Apr 1 21:59:00 2001 From: svg@REDACTED (Vladimir Sekissov) Date: Mon, 02 Apr 2001 01:59:00 +0600 Subject: ANN: Event Modeling Language plug-in for Dia Message-ID: <20010402015900D.svg@disney.surnet.ru> Good day. I wrote Event Modeling Language diagram plug-in for Dia (freely available Visio's analog) as described in Maurice Castro paper. It was developed under Linux and tested with dia 0.86 and CVS versions. You can download plug-in and/or view screen-shorts from http://disney.surnet.ru/dia Please, send me your comments and suggestions. Regards, Vladimir Sekissov From twiddles88@REDACTED Tue Apr 3 04:15:57 2001 From: twiddles88@REDACTED (Sarah C. Wood) Date: Mon, 2 Apr 2001 22:15:57 -0400 Subject: Engineering Assignment Message-ID: <20010402.221559.-321313.0.Twiddles88@juno.com> If I have 1320 hours of traffic in the busiest month of the year... What is an average days traffic? I got 60 How much traffic is in the Busy Hour? I got 10 hours How many trunks are required for a P.02 GOS? I got 17 trunks... How many trunks are required for a P.04 GOS ? I got 16 trunks... How many trunks are required for a P.10 GOS? I got 14 trunks... Last question: Why would I require a P.02 GOS rather than a P.10 GOS? Would the answer be the more trunks the better??? Anyone help....please tell me if I am right or wrong?? From codivina@REDACTED Tue Apr 3 02:31:41 2001 From: codivina@REDACTED (=?iso-8859-1?Q?Col=E9gio_Divina_Provid=EAncia?=) Date: Mon, 2 Apr 2001 21:31:41 -0300 Subject: Help-me Message-ID: <010801c0bbd5$75832110$0200a0c0@DDIVINA.LOCAL> Dear Srs. Please, I'm a graduate student of Computer Cienci in State of Santa Catarina Brazil, I need developer a Work of Erlang Language. Wel, I like know who I do array in Erlang? and I need developer a work of producer and consumer whit erlang using semaphores Please Reply me and help-me.. Thangs ... Marcio Luiz Ferreira Studant of Computing Cience of University of FURB Fundation University Regionary of Blumeau SC - Brasil My E.-mails are marciolf@REDACTED marcio_co@REDACTED codivina@REDACTED -------------- next part -------------- An HTML attachment was scrubbed... URL: From raimo@REDACTED Tue Apr 3 07:50:58 2001 From: raimo@REDACTED (Raimo Niskanen) Date: Tue, 03 Apr 2001 07:50:58 +0200 Subject: Engineering Assignment References: <20010402.221559.-321313.0.Twiddles88@juno.com> Message-ID: <3AC964C2.A94C39A6@erix.ericsson.se> "Sarah C. Wood" wrote: > > If I have 1320 hours of traffic in the busiest month of the year... > > What is an average days traffic? I got 60 > > How much traffic is in the Busy Hour? I got 10 hours > > How many trunks are required for a P.02 GOS? I got 17 trunks... > How many trunks are required for a P.04 GOS ? I got 16 trunks... > How many trunks are required for a P.10 GOS? I got 14 trunks... > > Last question: Why would I require a P.02 GOS rather than a P.10 GOS? > > Would the answer be the more trunks the better??? > > Anyone help....please tell me if I am right or wrong?? I think you have got the wrong mailing list. This one is for the Erlang programming language, and it has not got much in common with the unit for traffic load on a PABX, other than that it is named after the same Danish matematician, and that the Erlang programming language can be used for programming PABXes. / Raimo Niskanen From raimo@REDACTED Tue Apr 3 08:07:19 2001 From: raimo@REDACTED (Raimo Niskanen) Date: Tue, 03 Apr 2001 08:07:19 +0200 Subject: Help-me References: <010801c0bbd5$75832110$0200a0c0@DDIVINA.LOCAL> Message-ID: <3AC96897.7BA0B83F@erix.ericsson.se> Well, you can't do arrays in Erlang. Since Erlang is a functional programming language, mutable data structures (such as arrays) are not available in the language. The closest resembling data structures are ETS tables and the process dictionary. Both are accessed through BIFs (Built In Functions). The process dictionary can be quite a fast array if it is not too big. ... and semaphores do not exist either, since Erlang uses asynchronous message passing between processes, and the processes can (almost) only interact through this message passing. Therefore processes have no common data and do not need semaphores to synchronize updates of common data areas. Exception: ETS tables can be common to several processes and updates might need to be synchronized. This is often solved by feeding all updates (using message passing) through one process - eliminating collisions. If you really need a semaphore to control the access of e.g some external resource, one can easily be written as a locker process that holds the semaphore state and is accessed through message passing, or, again, have one single process that controls the external resource. I hope this information gives some clarity to you. / Raimo Niskanen, Ericsson UAB, Erlang/OTP > Col?gio Divina Provid?ncia wrote: > > Dear Srs. > Please, I'm a graduate student of Computer Cienci in > State of Santa Catarina Brazil, I need developer a Work of > Erlang Language. > Wel, I like know who I do array in Erlang? > and I need developer a work of producer and consumer whit erlang > using semaphores > Please Reply me and help-me.. > > > Thangs ... > > Marcio Luiz Ferreira > Studant of Computing Cience of University of FURB > Fundation University Regionary of Blumeau SC - Brasil > My E.-mails are > marciolf@REDACTED > marcio_co@REDACTED > codivina@REDACTED > From etxuwig@REDACTED Tue Apr 3 09:52:57 2001 From: etxuwig@REDACTED (Ulf Wiger) Date: Tue, 3 Apr 2001 09:52:57 +0200 (MET DST) Subject: Help-me In-Reply-To: <3AC96897.7BA0B83F@erix.ericsson.se> Message-ID: On Tue, 3 Apr 2001, Raimo Niskanen wrote: >Well, > >you can't do arrays in Erlang. Since Erlang is a functional >programming language, mutable data structures (such as arrays) are >not available in the language. The closest resembling data >structures are ETS tables and the process dictionary. Both are >accessed through BIFs (Built In Functions). The process dictionary >can be quite a fast array if it is not too big. For small arrays, you can use a tuple: array(Size) -> erlang:make_tuple(Size). read(Pos, Array) -> element(Pos, Array). write(Pos, Array, Value) -> setelement(Pos, Array, Value). But performance for write/3 will not be that great for bigger arrays. On http://www.erlang.org, under "User Contributions", you can find something called dynarray. It's a dynamically sized array-like structure that works like the small example above. Or, use ETS or process dictionary as Raimo suggests. Perhaps you could clarify why you need a semaphore? /Uffe -- Ulf Wiger tfn: +46 8 719 81 95 Senior System Architect mob: +46 70 519 81 95 Strategic Product & System Management ATM Multiservice Networks Data Backbone & Optical Services Division Ericsson Telecom AB From rv@REDACTED Tue Apr 3 11:16:51 2001 From: rv@REDACTED (Robert Virding) Date: Tue, 03 Apr 2001 11:16:51 +0200 Subject: Help-me In-Reply-To: Your message of "Tue, 03 Apr 2001 09:52:57 +0200." Message-ID: <200104030916.LAA16400@trana.bluetail.com> Ulf Wiger writes: >On Tue, 3 Apr 2001, Raimo Niskanen wrote: > >For small arrays, you can use a tuple: > >array(Size) -> erlang:make_tuple(Size). > >read(Pos, Array) -> element(Pos, Array). > >write(Pos, Array, Value) -> setelement(Pos, Array, Value). > > >But performance for write/3 will not be that great for bigger arrays. > >On http://www.erlang.org, under "User Contributions", you can find >something called dynarray. It's a dynamically sized array-like >structure that works like the small example above. > >Or, use ETS or process dictionary as Raimo suggests. The standard dict module in stdlib will do the same thing as dynarray and it has the added benefit that not only does it expand on demand it will also compact when it can. Robert From etxuwig@REDACTED Tue Apr 3 17:18:32 2001 From: etxuwig@REDACTED (Ulf Wiger) Date: Tue, 3 Apr 2001 17:18:32 +0200 (MET DST) Subject: bit syntax documentation Message-ID: In the documentation for the bit syntax, I read the following: "A segment of type binary must have a size evenly divisible by 8. This means that the following head will never match: foo(<>) ->" (http://www.erlang.org/doc/r7b/doc/extensions/bit_syntax.html#6.6) Since my code didn't work as expected, I tried to verify this: 30> F = fun(<>) -> {X,Y}; (Other) -> Other end. #Fun 31> F(<<"12345678">>). {<<49,50,51,52,53,54,55>>,<<56>>} In other words, the head described in the manual matches just fine. Comments? /Uffe -- Ulf Wiger tfn: +46 8 719 81 95 Senior System Architect mob: +46 70 519 81 95 Strategic Product & System Management ATM Multiservice Networks Data Backbone & Optical Services Division Ericsson Telecom AB From bjorn@REDACTED Tue Apr 3 17:26:35 2001 From: bjorn@REDACTED (Bjorn Gustavsson) Date: 03 Apr 2001 17:26:35 +0200 Subject: bit syntax documentation In-Reply-To: Ulf Wiger's message of "Tue, 3 Apr 2001 17:18:32 +0200 (MET DST)" References: Message-ID: Thanks for pointing out the error. The head should have been foo(<>) -> We will correct the documentation in the R8 release. /Bjorn Ulf Wiger writes: > In the documentation for the bit syntax, I read the following: > > > "A segment of type binary must have a size evenly divisible by 8. > > This means that the following head will never match: > > foo(<>) ->" > > (http://www.erlang.org/doc/r7b/doc/extensions/bit_syntax.html#6.6) > > Since my code didn't work as expected, I tried to verify this: > > > > 30> F = fun(<>) -> {X,Y}; (Other) -> Other > end. > #Fun > 31> F(<<"12345678">>). > {<<49,50,51,52,53,54,55>>,<<56>>} > > > > In other words, the head described in the manual matches just fine. > > Comments? > > /Uffe > -- > Ulf Wiger tfn: +46 8 719 81 95 > Senior System Architect mob: +46 70 519 81 95 > Strategic Product & System Management ATM Multiservice Networks > Data Backbone & Optical Services Division Ericsson Telecom AB > -- Bj?rn Gustavsson Ericsson Utvecklings AB bjorn@REDACTED ?T2/UAB/F/P BOX 1505 +46 8 727 56 87 125 25 ?lvsj? From rv@REDACTED Tue Apr 3 17:30:23 2001 From: rv@REDACTED (Robert Virding) Date: Tue, 03 Apr 2001 17:30:23 +0200 Subject: bit syntax documentation In-Reply-To: Your message of "03 Apr 2001 17:26:35 +0200." Message-ID: <200104031530.RAA17404@trana.bluetail.com> Bjorn Gustavsson writes: >Thanks for pointing out the error. > >The head should have been > > foo(<>) -> > >We will correct the documentation in the R8 release. The default unit for binaries is 8, i.e. bytes. For integers and floats the default unit is 1. Robert From Sean.Hinde@REDACTED Wed Apr 4 19:32:18 2001 From: Sean.Hinde@REDACTED (Sean Hinde) Date: Wed, 4 Apr 2001 18:32:18 +0100 Subject: Retrieving vsn attribute from loaded modules Message-ID: <402DD461F109D411977E0008C791C312039F5EBC@imp02mbx.one2one.co.uk> Hi, I've never figured out how to retrieve the vsn attribute from a loaded module. beam_lib gives this for beam files but I've struck a blank for already loaded code. Any ideas anyone? Thanks, Sean NOTICE AND DISCLAIMER: This email (including attachments) is confidential. If you have received this email in error please notify the sender immediately and delete this email from your system without copying or disseminating it or placing any reliance upon its contents. We cannot accept liability for any breaches of confidence arising through use of email. Any opinions expressed in this email (including attachments) are those of the author and do not necessarily reflect our opinions. We will not accept responsibility for any commitments made by our employees outside the scope of our business. We do not warrant the accuracy or completeness of such information. From peter@REDACTED Wed Apr 4 20:25:52 2001 From: peter@REDACTED (Peter H|gfeldt) Date: Wed, 4 Apr 2001 20:25:52 +0200 (MET DST) Subject: Retrieving vsn attribute from loaded modules In-Reply-To: <402DD461F109D411977E0008C791C312039F5EBC@imp02mbx.one2one.co.uk> Message-ID: Try Module:module_info(attributes). which returns a list of key-value pairs of attributes, in particular the vsn attribute. Here Module is the name of the module in question. /Peter ------------------------------------------------------------------------- Peter H?gfeldt e-mail : peter@REDACTED Open Telecom Platform Phone: : +46 (8) 727 57 58 Ericsson Utvecklings AB Mobile : +46 070-519 57 51 S-126 25 STOCKHOLM Fax: : +46 (8) 727 5775 Office address: Armborstv?gen 1, ?lvsj? On Wed, 4 Apr 2001, Sean Hinde wrote: > Hi, > > I've never figured out how to retrieve the vsn attribute from a loaded > module. > > beam_lib gives this for beam files but I've struck a blank for already > loaded code. > > Any ideas anyone? > > Thanks, > > Sean > > > > NOTICE AND DISCLAIMER: > This email (including attachments) is confidential. If you have received > this email in error please notify the sender immediately and delete this > email from your system without copying or disseminating it or placing any > reliance upon its contents. We cannot accept liability for any breaches of > confidence arising through use of email. Any opinions expressed in this > email (including attachments) are those of the author and do not necessarily > reflect our opinions. We will not accept responsibility for any commitments > made by our employees outside the scope of our business. We do not warrant > the accuracy or completeness of such information. > > From Sean.Hinde@REDACTED Wed Apr 4 20:27:13 2001 From: Sean.Hinde@REDACTED (Sean Hinde) Date: Wed, 4 Apr 2001 19:27:13 +0100 Subject: Retrieving vsn attribute from loaded modules Message-ID: <402DD461F109D411977E0008C791C312039F5EBD@imp02mbx.one2one.co.uk> > > Try > > Module:module_info(attributes). > > which returns a list of key-value pairs of attributes, in particular > the vsn attribute. Here Module is the name of the module in question. > > /Peter Thanks very much. That's the job.. Sean NOTICE AND DISCLAIMER: This email (including attachments) is confidential. If you have received this email in error please notify the sender immediately and delete this email from your system without copying or disseminating it or placing any reliance upon its contents. We cannot accept liability for any breaches of confidence arising through use of email. Any opinions expressed in this email (including attachments) are those of the author and do not necessarily reflect our opinions. We will not accept responsibility for any commitments made by our employees outside the scope of our business. We do not warrant the accuracy or completeness of such information. From etxuwig@REDACTED Wed Apr 4 20:32:18 2001 From: etxuwig@REDACTED (Ulf Wiger) Date: Wed, 4 Apr 2001 20:32:18 +0200 (MET DST) Subject: Retrieving vsn attribute from loaded modules In-Reply-To: <402DD461F109D411977E0008C791C312039F5EBC@imp02mbx.one2one.co.uk> Message-ID: -module(foo). -compile(export_all). -vsn("1.2.3"). ... Eshell V5.0.1 (abort with ^G) 1> foo:module_info(attributes). [{vsn,"1.2.3"}] 2> /Uffe On Wed, 4 Apr 2001, Sean Hinde wrote: >Hi, > >I've never figured out how to retrieve the vsn attribute from a loaded >module. > >beam_lib gives this for beam files but I've struck a blank for already >loaded code. > >Any ideas anyone? > >Thanks, > >Sean > > > >NOTICE AND DISCLAIMER: >This email (including attachments) is confidential. If you have received >this email in error please notify the sender immediately and delete this >email from your system without copying or disseminating it or placing any >reliance upon its contents. We cannot accept liability for any breaches of >confidence arising through use of email. Any opinions expressed in this >email (including attachments) are those of the author and do not necessarily >reflect our opinions. We will not accept responsibility for any commitments >made by our employees outside the scope of our business. We do not warrant >the accuracy or completeness of such information. > > > -- Ulf Wiger tfn: +46 8 719 81 95 Senior System Architect mob: +46 70 519 81 95 Strategic Product & System Management ATM Multiservice Networks Data Backbone & Optical Services Division Ericsson Telecom AB From ahltorp@REDACTED Thu Apr 5 19:56:40 2001 From: ahltorp@REDACTED (Magnus Ahltorp) Date: 05 Apr 2001 19:56:40 +0200 Subject: SIP proxy Message-ID: I just wrote a SIP proxy in Erlang. It is available under a BSD-type license, and is a prototype at the moment, but I hope to productify it in a few days. Time for learning Erlang and and writing it: 1 week. Features at the moment: * Redirect * Proxy * Option to make all signalling messages go through the proxy (with Record-route) * Voicemail * Remote administration (custom r-e-p-l on other node) * Web interface * Dynamic registration of phones * MD5 digest auth of phone registrations and outgoing calls * Static forwarding of calls /Magnus From hal@REDACTED Thu Apr 5 23:24:14 2001 From: hal@REDACTED (Hal Snyder) Date: 05 Apr 2001 16:24:14 -0500 Subject: SIP proxy In-Reply-To: Magnus Ahltorp's message of "05 Apr 2001 19:56:40 +0200" References: Message-ID: <8766gjyr9d.fsf@ghidra.vail> Magnus Ahltorp writes: > I just wrote a SIP proxy in Erlang. It is available under a BSD-type > license, and is a prototype at the moment, but I hope to productify > it in a few days. Fascinating. Bring it on! From thomas@REDACTED Fri Apr 6 09:29:21 2001 From: thomas@REDACTED (Thomas Arts) Date: Fri, 06 Apr 2001 09:29:21 +0200 Subject: erl_id_trans.erl Message-ID: <3ACD7051.8E6A8E0F@cslab.ericsson.se> Bumped into the following: in erl_id_trans.erl the function record_updates record_updates([{record_field,Lf,{atom,La,F},Val0}|Us]) -> Val1 = expr(Val0), [{record_field,Lf,{atom,La,F},Val0}|record_updates(Us)]; record_updates([]) -> []. should read Val1 instead of Val0 in the third line, i.e: record_updates([{record_field,Lf,{atom,La,F},Val0}|Us]) -> Val1 = expr(Val0), [{record_field,Lf,{atom,La,F},Val1}|record_updates(Us)]; record_updates([]) -> []. Regards Thomas --- Thomas Arts Computer Science Lab From cpressey@REDACTED Fri Apr 6 19:15:44 2001 From: cpressey@REDACTED (Chris Pressey) Date: Fri, 06 Apr 2001 12:15:44 -0500 Subject: erl_id_trans.erl References: <3ACD7051.8E6A8E0F@cslab.ericsson.se> Message-ID: <3ACDF9C0.370F6B2A@catseye.mb.ca> Thomas Arts wrote: > Bumped into the following: > in erl_id_trans.erl > the function record_updates > record_updates([{record_field,Lf,{atom,La,F},Val0}|Us]) -> > Val1 = expr(Val0), > [{record_field,Lf,{atom,La,F},Val0}|record_updates(Us)]; > record_updates([]) -> []. > should read Val1 instead of Val0 in the third line, i.e: Perhaps some future version of the compiler could warn against unused variable bindings? This is a feature that is in Perl, but I find it somewhat annoying to me there (as it does not match my imperative/procedural programming style, where mutable variables can be updated arbitrarily anyway.) But in Erlang is would actually make a lot more sense and I think I would like it a lot. _chris -- "Ten short days ago all I could look forward to was a dead-end job as a engineer. Now I have a promising future and make really big Zorkmids." Chris Pressey, Cat's Eye Technologies, http://www.catseye.mb.ca/ Esoteric Topics Mailing List: http://www.catseye.mb.ca/list.html From rv@REDACTED Mon Apr 9 02:01:59 2001 From: rv@REDACTED (Robert Virding) Date: Mon, 09 Apr 2001 02:01:59 +0200 Subject: erl_id_trans.erl In-Reply-To: Your message of "Fri, 06 Apr 2001 12:15:44 CDT." <3ACDF9C0.370F6B2A@catseye.mb.ca> Message-ID: <200104090001.f3901xg22049@duva.bluetail.com> Chris Pressey writes: > >Perhaps some future version of the compiler could warn against unused >variable bindings? This is a feature that is in Perl, but I find it >somewhat annoying to me there (as it does not match my >imperative/procedural programming style, where mutable variables can be >updated arbitrarily anyway.) > >But in Erlang is would actually make a lot more sense and I think I >would like it a lot. This already exists. If you give the option 'warn_unused_vars' to the compiler it will list all variables that are never used EXCEPT those whose name starts with "_", e.g. _Foo or _bar. The rationale behind this is that it gives you a way of using meaningful names instead of "_" for variables you neve use. I personally don't like this option as it does not suit my style of using variable names. For an example of what can happen with code not tuned to this option recompile erl_lint.erl. Some other useful checking options are 'warn_unused_imports' and 'warn_format' for checking imports and calls to io:fwrite/format. The last can also take different values {warn_format,0-3} where 3 checks the most. Robert -- Robert Virding Tel: +46 (0)8 692 22 12 Bluetail AB Email: rv@REDACTED Hantverkargatan 78 WWW: http://www.bluetail.com/~rv SE-112 38 Stockholm, SWEDEN "Folk s?ger att jag inte bryr mig om n?gonting, men det skiter jag i". From vladdu@REDACTED Mon Apr 9 08:58:35 2001 From: vladdu@REDACTED (Vlad Dumitrescu) Date: Mon, 09 Apr 2001 08:58:35 +0200 Subject: connecting nodes Message-ID: Hi all! I have just finished to write the small application required for the Erlang certification, and it was interesting as it made me work with some things that I never did before... And some questions came up. Maybe you can shed some light: *** As far as I can tell, there isn't a way to automatically connect nodes. In order to (for example) access a global server and allow for it's node to crash and come back online transparently, one has to know that node's name and it's host name. I don't really like the idea of hardcoding the node/host names... Or one has to have a central registrar node which all new nodes must connect to, but that is just moving away the problem, because that central node might crash too. Is it as I think, that there is no way to go around this problem, or have I missed something? *** Another question is about io. If there is a process reading from the standard input, is there any way to cancel that input request and let the process keep running? Cheers, Vlad _________________________________________________________________________ Get Your Private, Free E-mail from MSN Hotmail at http://www.hotmail.com. From mbj@REDACTED Mon Apr 9 09:32:08 2001 From: mbj@REDACTED (Martin Bjorklund) Date: Mon, 09 Apr 2001 09:32:08 +0200 Subject: connecting nodes In-Reply-To: Your message of "Mon, 09 Apr 2001 08:58:35 +0200" References: Message-ID: <20010409093208T.mbj@bluetail.com> "Vlad Dumitrescu" wrote: > > *** > As far as I can tell, there isn't a way to automatically connect > nodes. In order to (for example) access a global server and allow > for it's node to crash and come back online transparently, one has > to know that node's name and it's host name. There are two sides of this problem. First, there is inital connection. For this, you'll have to provide the node name to the system in some way. Once you have done this, global can be used to automatically set up a fully connected net. There is no auto-discovery mechanism in the standard distribution, but it is quite simple to write your own, either using broadcast or multicast. [We're using a broadcast mechanism for some nodes in our systems.] You might have to think about security issues though. It all depends on the application. Second, once all nodes have contact, you'd like to make sure that all nodes keep their connections. If one node crashes and restart, you're back to initial start, which can be handled. A worse situation is if you have at least three nodes, and because of network/host loads, one of the TCP connections times out. In this case, you don't have a fully connected net anymore, and global stops working(*). Probably Mnesia as well. This is a big defect in global(**). [In our system, each node runs a 'pinger' process, which starts to periodically ping each node as it goes down, until it either comes back up, or is removed from the system. Once it's up again, you might end up in with a partitioned network which regained it's contact, which is another difficult problem to solve. We solve it by restarting one of the partitions, and some db magic :) ] [*] Unfortunately, it doesn't even detect this situation, so the result might be that the name registry becomes inconsistent, or that global:sync() hangs (which means that the global handshake procedure hangs or failed), or it crashes (which is the best of the three). [**] Since I designed one incaration of global, you can blame me ;) > I don't really like the idea of hardcoding the node/host names... You should never have to do that of course. [In our system, each node is added by an operator (he doesn't know he's adding an Erlang node of course), which provides the IP address of another box in the system. We contact the node on that box, and store the new node name in the configuration files in the rest of the system. Auto-discovery could be used instead, and we probably will do that for some special systems in the future.] /martin From luke@REDACTED Mon Apr 9 12:22:55 2001 From: luke@REDACTED (Luke Gorrie) Date: 09 Apr 2001 12:22:55 +0200 Subject: Presenting "Ermacs" Message-ID: Hallo, I've made an Emacs clone in Erlang. It's just reached self-hackability, so I think it's time to share it. It can edit large files, has a built-in erlang shell, does erlang syntactic indentation, and various other emacs-like things. Obviously it doesn't do everything that emacs does just yet :-) Download instructions at http://www.bluetail.com/~luke/ermacs/ It requires you to have the "S-Lang" terminal library installed (which you probably already do). http://www.s-lang.org/ Hope you like it :-) feedback and patches welcome. Cheerio, Luke From etxuwig@REDACTED Mon Apr 9 13:20:17 2001 From: etxuwig@REDACTED (Ulf Wiger) Date: Mon, 9 Apr 2001 13:20:17 +0200 (MET DST) Subject: connecting nodes In-Reply-To: <20010409093208T.mbj@bluetail.com> Message-ID: On Mon, 9 Apr 2001, Martin Bjorklund wrote: >A worse situation is if >you have at least three nodes, and because of network/host loads, one >of the TCP connections times out. In this case, you don't have a >fully connected net anymore, and global stops working(*). Probably >Mnesia as well. This is a big defect in global(**). [In our system, >each node runs a 'pinger' process, which starts to periodically ping >each node as it goes down, until it either comes back up, or is >removed from the system. Once it's up again, you might end up in with >a partitioned network which regained it's contact, which is another >difficult problem to solve. We solve it by restarting one of the >partitions, and some db magic :) ] In our system, the AXD 301, we do something similar, but also enable the flag 'kernel -dist_auto_connect once', in order to handle partitioned networks in a controlled manner. This flag makes sure that two nodes can't reconnect, once separated, without at least one of the nodes restarting. In addition to this, we have a "backdoor ping" (UDP-based) to detect communication failures: if we get a ping from a known node that's not in the node list, we have a partitioned network. One way to handle the auto-connect problem could be to let mnesia connect. If your system is set up so that you have a few mnesia nodes that handle the persistent database, and other nodes that just run diskless mnesia clients, you can start the diskless clients with -mnesia extra_db_nodes '. Then, the diskless clients will attempt to find at least one of the persistent nodes in order to retrieve the mnesia schema. /Uffe -- Ulf Wiger tfn: +46 8 719 81 95 Senior System Architect mob: +46 70 519 81 95 Strategic Product & System Management ATM Multiservice Networks Data Backbone & Optical Services Division Ericsson Telecom AB From gerd@REDACTED Mon Apr 9 16:19:29 2001 From: gerd@REDACTED (Gerd Flaig) Date: 09 Apr 2001 16:19:29 +0200 Subject: erl_call Message-ID: Hi, I noticed some strange behaviour with erl_call (erl_interface versions 3.2 and 3.2.9). When there is more than one instance of erl_call running concurrently against the same erlang node, there is a rather high possibility that one of them terminates with an error. -module(test). -export([start/0,count/0,count/1,incr/0]). start() -> Pid = spawn_link(test, count, []), true = register(counter, Pid). count() -> count(0). count(N) -> receive {incr, Pid} -> Pid ! {count, N+1}, test:count(N+1); Other -> io:format('unknown: ~w~n', [Other]) end, test:count(N). incr() -> counter ! {incr, self()}, receive {count, N} -> N; Other -> io:format('unknown: ~w~n', [Other]) end. im% erl -name test Erlang (BEAM) emulator version 5.0.2 [source] Eshell V5.0.2 (abort with ^G) (test@REDACTED)1> c(test). {ok,test} (test@REDACTED)2> test:start(). true im% cat test.pl #! /usr/bin/perl -w $erlcall = 'echo \'test:incr().\' | /usr/lib/erlang/lib/erl_interface-3.2.9/bin/erl_call -e -n test@REDACTED'; $last = 0; for(;;) { ($n) = `$erlcall` =~ m/{ok,(\d+)}/; if($n - $last != 1) { print "n: $n, last: $last\n"; } $last = $n; } When I run test.pl, I get lots of these: erl_connect failed Any ideas? Goodbyte, Gerd. -- Gerd Flaig Technik gerd@REDACTED Bei Schlund + Partner AG Erbprinzenstr. 4-12 D-76133 Karlsruhe From srl@REDACTED Tue Apr 10 12:12:39 2001 From: srl@REDACTED (Steve Langstaff) Date: Tue, 10 Apr 2001 11:12:39 +0100 Subject: Robustness of IC and erl_interface Message-ID: <01C0C1AF.285EC170.srl@terminus.ericsson.se> Hi all. Question for all you IC/erl_interface gurus out there: For a call from a C-node to an Erlang genserver (names changed to protect the guilty!): module mod { interface intf { void call( in unsigned long param1, in unsigned long param2, in unsigned long param3, in unsigned long param4, in unsigned long param5, in unsigned long param6, in unsigned long param7 ); } } IC generates something like the following code: void mod_intf_call(mod_intf oe_obj, CORBA_unsigned_long param1, CORBA_unsigned_long param2, CORBA_unsigned_long param3, CORBA_unsigned_long param4, CORBA_unsigned_long param5, CORBA_unsigned_long param6, CORBA_unsigned_long param7, CORBA_Environment *oe_env) { int oe_msgType = 0; erlang_msg oe_msg; /* Initiating the message reference */ ic_init_ref(oe_env,&oe_env->_unique); /* Initiating exception indicator */ oe_env->_major = CORBA_NO_EXCEPTION; /* Creating call message */ if (mod_intf_call__client_enc(oe_obj, param1, param2, param3, param4, param5, param6, param7, oe_env) < 0) { if (oe_env->_major == CORBA_NO_EXCEPTION) CORBA_exc_set(oe_env, CORBA_SYSTEM_EXCEPTION, MARSHAL, "Cannot encode message"); } /* Sending call request */ if (oe_env->_major == CORBA_NO_EXCEPTION) { if (strlen(oe_env->_regname) == 0) { if (ei_send_encoded(oe_env->_fd, oe_env->_to_pid, oe_env->_outbuf, oe_env->_iout) < 0) { CORBA_exc_set(oe_env, CORBA_SYSTEM_EXCEPTION, NO_RESPONSE, "Cannot connect to server"); } } else if (ei_send_reg_encoded(oe_env->_fd, oe_env->_from_pid, oe_env->_regname, oe_env->_outbuf, oe_env->_iout) < 0) { CORBA_exc_set(oe_env, CORBA_SYSTEM_EXCEPTION, NO_RESPONSE, "Cannot connect to server"); } } /* Receiving reply message */ if (oe_env->_major == CORBA_NO_EXCEPTION) do { if ((oe_msgType = ei_receive_encoded(oe_env->_fd, &oe_env->_inbuf, &oe_env->_inbufsz, &oe_msg, &oe_env->_iin)) < 0) { CORBA_exc_set(oe_env, CORBA_SYSTEM_EXCEPTION, MARSHAL, "Cannot decode message"); break; } } while (oe_msgType != ERL_SEND && oe_msgType != ERL_REG_SEND); /* Extracting message header */ if (oe_env->_major == CORBA_NO_EXCEPTION) if (mod_intf__receive_info(oe_obj, oe_env) < 0) { CORBA_exc_set(oe_env, CORBA_SYSTEM_EXCEPTION, MARSHAL, "Bad message"); } /* Extracting message tail */ if (oe_env->_major == CORBA_NO_EXCEPTION) if (mod_intf_call__client_dec(oe_obj, oe_env) < 0) { CORBA_exc_set(oe_env, CORBA_SYSTEM_EXCEPTION, MARSHAL, "Bad message tail"); } } This works OK for the simple case, but what happens if my genserver exits before sending a response (or the node that it's on fails, or...)? Will my C-node be hung waiting for a response until TCP/IP times out (maybe a couple of hours)? Any ideas on how I could make this more responsive to failure, maybe with a client-side timeout specified somehow? -- Steve L. From tonyp@REDACTED Tue Apr 10 13:08:01 2001 From: tonyp@REDACTED (Tony Pedley) Date: Tue, 10 Apr 2001 12:08:01 +0100 Subject: gen_server code_change Message-ID: <3AD2E991.87382D73@terminus.ericsson.se> Hi, I had always assumed up to now that the gen_server callback function code_change was called automatically when a new gen_server module was loaded into the Erlang shell(i.e l(Module)) This was partly due to my reading of the Erlang Reference Manual However in pursuit of a ongoing bug, I have begun to doubt that notion, even to the point of writing a simple gen_server and trying it. I have so far been unable to generate a code_change event. So the question is, in a gen_server does the code_change function have to be called explicitly by some release script or is it triggered by the actual process of loading a new module? If it is the latter is there any case when the function is not called? (version numbers, identical code size etc) Thanks Tony From peter@REDACTED Tue Apr 10 13:24:43 2001 From: peter@REDACTED (Peter H|gfeldt) Date: Tue, 10 Apr 2001 13:24:43 +0200 (MET DST) Subject: gen_server code_change In-Reply-To: <3AD2E991.87382D73@terminus.ericsson.se> Message-ID: Of the high-level code replacement instructions, only *update* can cause the code_change function to be called. It does that if and only if the Change element of the instruction is equal to {advanced, Extra}. /Peter ------------------------------------------------------------------------- Peter H?gfeldt e-mail : peter@REDACTED Open Telecom Platform Phone: : +46 (8) 727 57 58 Ericsson Utvecklings AB Mobile : +46 070-519 57 51 S-126 25 STOCKHOLM Fax: : +46 (8) 727 5775 Office address: Armborstv?gen 1, ?lvsj? On Tue, 10 Apr 2001, Tony Pedley wrote: > Hi, > I had always assumed up to now that the gen_server callback function > code_change was called > automatically when a new gen_server module was loaded into the Erlang > shell(i.e l(Module)) > This was partly due to my reading of the Erlang Reference Manual > > However in pursuit of a ongoing bug, I have begun to doubt that notion, > even to the point > of writing a simple gen_server and trying it. I have so far been unable > to generate a code_change event. > > So the question is, in a gen_server does the code_change function have > to be called explicitly by some > release script or is it triggered by the actual process of loading a new > module? > > If it is the latter is there any case when the function is not called? > (version numbers, identical code size etc) > > Thanks > > Tony > From etxuwig@REDACTED Tue Apr 10 13:25:54 2001 From: etxuwig@REDACTED (Ulf Wiger) Date: Tue, 10 Apr 2001 13:25:54 +0200 (MET DST) Subject: gen_server code_change In-Reply-To: <3AD2E991.87382D73@terminus.ericsson.se> Message-ID: On Tue, 10 Apr 2001, Tony Pedley wrote: >I had always assumed up to now that the gen_server callback function >code_change was called automatically when a new gen_server module >was loaded into the Erlang shell(i.e l(Module)) This was partly due >to my reading of the Erlang Reference Manual You need to read the SASL User's Guide: http://www.erlang.org/doc/r7b/lib/sasl-1.9/doc/html/part_frame.html (but that's pretty hard reading, so read this whole message first.) Specifically, there's an example on upgrading a gen_server at http://www.erlang.org/doc/r7b/lib/sasl-1.9/doc/html/release_handling.html#4.7 >However in pursuit of a ongoing bug, I have begun to doubt that >notion, even to the point of writing a simple gen_server and trying >it. I have so far been unable to generate a code_change event. Try this: sys:suspend(MyProc). sys:change_code(MyProc, OldVsn, Module, Extra). sys:resume(MyProc). These functions are called by the release_handler during upgrade, triggered by instructions in the release handling script, like: [{suspend, [Module]}, {code_change, [{Module, []}]} {resume, [Module]}] In the script, one specifies the module names, and the release_handler finds out at runtime which processes are related to the modules being upgraded. This relation is specified in the supervisor child specification. OldVsn corresponds to the -vsn attribute in the code, according to the manual. I haven't checked if this is still true, but I know that OTP has removed the -vsn attribute from all their source files. The reason was that it made automatic merge between development branches difficult. At AXD301, we ignore the OldVsn argument and put interesting information in Extra instead. In our system, Extra is {OldAppVsn, NewAppVsn}, where these versions correspond to the 'vsn' attribute in the .app files. /Uffe -- Ulf Wiger tfn: +46 8 719 81 95 Senior System Architect mob: +46 70 519 81 95 Strategic Product & System Management ATM Multiservice Networks Data Backbone & Optical Services Division Ericsson Telecom AB From etxuwig@REDACTED Tue Apr 10 13:47:32 2001 From: etxuwig@REDACTED (Ulf Wiger) Date: Tue, 10 Apr 2001 13:47:32 +0200 (MET DST) Subject: gen_server code_change In-Reply-To: <3AD2E991.87382D73@terminus.ericsson.se> Message-ID: On Tue, 10 Apr 2001, Tony Pedley wrote: >I had always assumed up to now that the gen_server callback function >code_change was called automatically when a new gen_server module >was loaded into the Erlang shell(i.e l(Module)) This was partly due >to my reading of the Erlang Reference Manual Oh, to answer this specific question, the only thing that happens when you call l(Module) in the shell is that the code is loaded. To simulate an upgrade of Module, you'd have to do this: sys:suspend(MyProc). l(Module). sys:change_code(MyProc, undefined, Module, Extra). sys:resume(MyProc). /Uffe -- Ulf Wiger tfn: +46 8 719 81 95 Senior System Architect mob: +46 70 519 81 95 Strategic Product & System Management ATM Multiservice Networks Data Backbone & Optical Services Division Ericsson Telecom AB From fred@REDACTED Wed Apr 11 15:04:57 2001 From: fred@REDACTED (Lars-Ake Fredlund) Date: Wed, 11 Apr 2001 15:04:57 +0200 Subject: Announcement: A formal verification tool for Erlang Message-ID: <3AD45679.E17B0E23@sics.se> Dear Erlang Users, we would like to announce the availability of a tool for the formal verification of software written in Erlang. The tool, henceforth called "The Erlang Verification Tool" (EVT), is a theorem-proving tool which has been augmented with support for (a subset of) the Erlang programming language, and with a graphical user interface. More information about the verification approach is available at http://www.sics.se/fdt/erlang/ and the tool itself can be dowloaded from http://www.sics.se/fdt/VeriCode/evt.html The people who have designed and programmed the tool, or have contributed by experimenting with the tool on case studies, include: Gennady Chugunov, Mads Dam, Lars-?ke Fredlund, Dilian Gurov (SICS) Thomas Arts, Clara Benac Earle (Ericsson CSLAB) Thomas Noll (RWTH Aachen) From Sean.Hinde@REDACTED Wed Apr 11 17:28:30 2001 From: Sean.Hinde@REDACTED (Sean Hinde) Date: Wed, 11 Apr 2001 16:28:30 +0100 Subject: Generic asn.1 parser Message-ID: <402DD461F109D411977E0008C791C312039F5EE3@imp02mbx.one2one.co.uk> Hi, I felt the need for an asn.1 parser which would decode generic asn.1 data into a tree structure. Here's the result in case anyone else might have felt this need! It seems to work fine for a whole bunch of c7 MAP messages (which include nested indefinite length constructor tags). I guess if it deals with those it should be in a reasonably working state :) The output format is a bit arbritrary. Don't sue me if it causes your business to fail etc. Please let me know if you improve it I use atoms quite a bit - does anyone have a good feel for the performance impact of this over using integers for matching and tagging? Rgds, Sean %%----------------------------------------------------------------------- %% Decode asn1 coded binary into parse tree %% Handles indefinite length if re-assembly has already been %% done - should be relatively easy to allow for segmented though %% as we keep a count of unrequited indefinite length %% constructor tags. %%----------------------------------------------------------------------- asn1_decode(Bin) -> asn1_decode(Bin, 0). asn1_decode(<<>>, 0) -> []; asn1_decode(Bin, N0) -> {Class, Form, Tag, Rest, N} = get_tag(Bin, N0), case tag_type(Class, Form, Tag) of indefinite_end -> asn1_decode(Rest, N); Constructor when Constructor == set; Constructor == seq; Constructor == constructor -> case get_length(Rest) of {indefinite, Rest1} -> [{{Constructor, indef, Class, Tag}, asn1_decode(Rest1, N+1)}]; {Len, Rest1} -> {Data, Rest2} = get_content(Len, Rest1), [{{Constructor, Class, Tag}, asn1_decode(Data, 0)}| asn1_decode(Rest2, N)] end; tag -> {Len, Rest1} = get_length(Rest), {Data, Rest2} = get_content(Len, Rest1), [{{tag, fmt_class(Class), Tag}, Data}|asn1_decode(Rest2, N)] end. %% Get tag data. 0:1, 0:15 gets around compiler %% bug as I haven't updated my PC yet.. get_tag(<<0:1, 0:15, Rest/binary>>, 0) -> exit(unexpected_end_of_indefinite_length); get_tag(<<0:1, 0:15, Rest/binary>>, N) -> {indefinite_end, 0, 0, Rest, N-1}; get_tag(<>, N) -> {Tag1, Rest1} = get_tag1(Tag, Rest), {Class, Form, Tag1, Rest1, N}. %% Handle extension parts of the tag field get_tag1(31, <<0:1, Tag:7, Rest/binary>>) -> {Tag, Rest}; get_tag1(31, <<1:1, Msb:7, _:1, Lsb:7, Rest/binary>>) -> {Msb*128+Lsb, Rest}; get_tag1(Tag, Rest) -> {Tag, Rest}. % Do short and long definite length forms % And *now*... indefinite length! get_length(<<0:1, Len:7, Rest/binary>>) -> {Len, Rest}; get_length(<<1:1, 0:7, Rest/binary>>) -> {indefinite, Rest}; get_length(<<1:1, Len_len:7, Rest/binary>>) -> <> = Rest, {Len, Rest1}. % Get actual content of field get_content(Len, Rest) -> <> = Rest, {Data, Rest1}. % tag_type(Class, Form, Tag) -> tag|seq|set|constructor tag_type(indefinite_end, _, _) -> indefinite_end; tag_type(Class, 0, Tag) -> tag; tag_type(0, 1, 16) -> seq; tag_type(0, 1, 17) -> set; tag_type(Class, 1, Els) -> constructor. fmt_class(0) -> univ; fmt_class(1) -> app; fmt_class(2) -> context; fmt_class(3) -> priv. NOTICE AND DISCLAIMER: This email (including attachments) is confidential. If you have received this email in error please notify the sender immediately and delete this email from your system without copying or disseminating it or placing any reliance upon its contents. We cannot accept liability for any breaches of confidence arising through use of email. Any opinions expressed in this email (including attachments) are those of the author and do not necessarily reflect our opinions. We will not accept responsibility for any commitments made by our employees outside the scope of our business. We do not warrant the accuracy or completeness of such information. From etxuwig@REDACTED Wed Apr 11 17:41:12 2001 From: etxuwig@REDACTED (Ulf Wiger) Date: Wed, 11 Apr 2001 17:41:12 +0200 (MET DST) Subject: Generic asn.1 parser In-Reply-To: <402DD461F109D411977E0008C791C312039F5EE3@imp02mbx.one2one.co.uk> Message-ID: On Wed, 11 Apr 2001, Sean Hinde wrote: >I use atoms quite a bit - does anyone have a good feel for the >performance impact of this over using integers for matching and >tagging? There should be no performance difference. /Uffe -- Ulf Wiger tfn: +46 8 719 81 95 Senior System Architect mob: +46 70 519 81 95 Strategic Product & System Management ATM Multiservice Networks Data Backbone & Optical Services Division Ericsson Telecom AB From Sean.Hinde@REDACTED Wed Apr 11 17:58:41 2001 From: Sean.Hinde@REDACTED (Sean Hinde) Date: Wed, 11 Apr 2001 16:58:41 +0100 Subject: Generic asn.1 parser Message-ID: <402DD461F109D411977E0008C791C312039F5EE6@imp02mbx.one2one.co.uk> > On Wed, 11 Apr 2001, Sean Hinde wrote: > > >I use atoms quite a bit - does anyone have a good feel for the > >performance impact of this over using integers for matching and > >tagging? > > There should be no performance difference. But doesn't it do some form of lookup in the atom table? Sean NOTICE AND DISCLAIMER: This email (including attachments) is confidential. If you have received this email in error please notify the sender immediately and delete this email from your system without copying or disseminating it or placing any reliance upon its contents. We cannot accept liability for any breaches of confidence arising through use of email. Any opinions expressed in this email (including attachments) are those of the author and do not necessarily reflect our opinions. We will not accept responsibility for any commitments made by our employees outside the scope of our business. We do not warrant the accuracy or completeness of such information. From raimo@REDACTED Thu Apr 12 10:22:27 2001 From: raimo@REDACTED (Raimo Niskanen) Date: Thu, 12 Apr 2001 10:22:27 +0200 Subject: Generic asn.1 parser References: <402DD461F109D411977E0008C791C312039F5EE6@imp02mbx.one2one.co.uk> Message-ID: <3AD565C3.5DEA737A@erix.ericsson.se> Sean Hinde wrote: > > > On Wed, 11 Apr 2001, Sean Hinde wrote: > > > > >I use atoms quite a bit - does anyone have a good feel for the > > >performance impact of this over using integers for matching and > > >tagging? > > > > There should be no performance difference. > > But doesn't it do some form of lookup in the atom table? > Internally, atoms are tagged integers. This means that comparisions for equality and pattern matches are just as simple as for integers. Construction of an atom, however, results in an insertion into the atom table, which is some kind of fast hash table. Compiled atoms are inserted in the atom table sometime when the code is loaded, thus possibly affecting the load time, marginally. What may be a catch is to construct atoms on the fly, and to construct an infinite number of them (due to random runtime data). This way you can exhaust the atom table since atoms are never removed therefrom. / Raimo Niskanen, Ericsson UAB, Erlang/OTP > Sean > > NOTICE AND DISCLAIMER: > This email (including attachments) is confidential. If you have received > this email in error please notify the sender immediately and delete this > email from your system without copying or disseminating it or placing any > reliance upon its contents. We cannot accept liability for any breaches of > confidence arising through use of email. Any opinions expressed in this > email (including attachments) are those of the author and do not necessarily > reflect our opinions. We will not accept responsibility for any commitments > made by our employees outside the scope of our business. We do not warrant > the accuracy or completeness of such information. From yvan.godin@REDACTED Fri Apr 13 08:36:40 2001 From: yvan.godin@REDACTED (yvan.godin@REDACTED) Date: Fri, 13 Apr 2001 08:36:40 +0200 (MEST) Subject: Erlang futures Message-ID: <987143800.3ad69e78b281a@imp.free.fr> Hello I have decided to develop a Workflow engine probably with Erlang but still not decided ;-) (I am also looking for OCAML and www.Open-Scheme.com) Erlang seem great but seem still suffer of some lacks .... I would like to know what improvments are planned in next R8 release GUI (GTK ???) Strings (with Binaries) ??? MNESIA fast enougth for big table (up to Giga records)??? File Upload for Inets Web server ??? (and why not some HIPE JIT parts incorporation ???) thanks for anwser (and sorry for my poor french/english) Yvan GODIN http://yvan.godin.free.fr EMail yvan.godin@REDACTED From Chandrashekhar.Mullaparthi@REDACTED Mon Apr 16 11:42:32 2001 From: Chandrashekhar.Mullaparthi@REDACTED (Chandrashekhar Mullaparthi) Date: Mon, 16 Apr 2001 10:42:32 +0100 Subject: Delete/Create in the same transaction Message-ID: <402DD461F109D411977E0008C791C31203919A67@imp02mbx.one2one.co.uk> 6> mnesia:create_table(bag, [{attributes, [a,b]}, {type, bag}, {disc_copies, [node()]}]). {atomic,ok} . . 18> mnesia:transaction(fun() -> mnesia:write({bag, 1, 2}) end). {atomic,ok} 19> 19> 19> mnesia:transaction(fun() -> mnesia:write({bag, 1, 3}) end). {atomic,ok} 20> 20> [mnesia:dirty_read({bag, X}) || X <- mnesia:dirty_all_keys(bag)]. [[{bag,1,2},{bag,1,3}]] 21> 21> mnesia:transaction(fun() -> mnesia:delete_object({bag, 1, 2}), mnesia:write({bag, 1, 2}) end). {atomic,ok} 22> 22> [mnesia:dirty_read({bag, X}) || X <- mnesia:dirty_all_keys(bag)]. [[{bag,1,3}]] In step 21, Why is it that if I delete an object in a transaction and write it back again in the same, the object doesn't exist in the database?? Intuitively it seems to be a bug.... cheers, Chandru NOTICE AND DISCLAIMER: This email (including attachments) is confidential. If you have received this email in error please notify the sender immediately and delete this email from your system without copying or disseminating it or placing any reliance upon its contents. We cannot accept liability for any breaches of confidence arising through use of email. Any opinions expressed in this email (including attachments) are those of the author and do not necessarily reflect our opinions. We will not accept responsibility for any commitments made by our employees outside the scope of our business. We do not warrant the accuracy or completeness of such information. From jouni.ryno@REDACTED Mon Apr 16 18:13:08 2001 From: jouni.ryno@REDACTED (Jouni Ryno) Date: Mon, 16 Apr 2001 19:13:08 +0300 Subject: Problem with bit syntax compilation Message-ID: >From the following code -module(test). error(Test) -> if Test == 1 -> Problem = 1; Test == 2 -> Problem = 2 end, Dummy = 1, OKbin = <>, % used only to prove, that it works normally MyBin = [<>, OKbin]. I get compilation error from erlc test.erl (eshell V5.0.2 /proj/cosima/egse/test.erl:none: internal error in v3_codegen; crash reason: {{case_clause,{'EXIT',{function_clause, [{v3_codegen,fetch_reg,['Problem ',[]]}, {v3_codegen,'-saves/3-fun-0-',3 }, {lists,map,2}, {v3_codegen,adjust_stack,4}, {v3_codegen,match_cg,6}, {v3_codegen,'-cg_list/5-fun-0-' ,3}, {v3_codegen,flatmapfoldl,3}, {v3_codegen,cg_list,5}| more]}}}, [{compile,'-select_passes/2-fun-2-',2}, {compile,'-internal_comp/4-fun-1-',2}, {compile,fold_comp,3}, {compile,internal_comp,4}, {compile,internal,3}]} Any way to circumvent this ? With regards Jouni Jouni Ryn? mailto://Jouni.Ryno@REDACTED/ http://www.geo.fmi.fi/~ryno/ Finnish Meteorological Institute http://www.fmi.fi/ Geophysical Research http://www.geo.fmi.fi/ P.O.BOX 503 Tel (+358)-9-19294656 FIN-00101 Helsinki FAX (+358)-9-19294603 Finland priv-GSM (+358)-50-5302903 "It's just zeros and ones, it cannot be hard" From dne@REDACTED Mon Apr 16 19:00:55 2001 From: dne@REDACTED (Daniel =?iso-8859-1?q?N=E9ri?=) Date: 16 Apr 2001 19:00:55 +0200 Subject: Problem with bit syntax compilation In-Reply-To: ("Jouni Ryno"'s message of "Mon, 16 Apr 2001 19:13:08 +0300") References: Message-ID: <87oftwlqyg.fsf@nowhere.mayonnaise.net> "Jouni Ryno" writes: > error(Test) -> > if > Test == 1 -> > Problem = 1; > Test == 2 -> > Problem = 2 > end, > Dummy = 1, > OKbin = <>, % used only to prove, that it works > normally > MyBin = [<>, OKbin]. > > I get compilation error from erlc test.erl (eshell V5.0.2 Although it does look like a bug, you missed the warning: /tmp/test.erl:12: Warning: variable 'Problem' exported from 'if' (line 4) So instead try: ,---- | error(Test) -> | Problem = if | Test == 1 -> | 1; | Test == 2 -> | 2 | end, | Dummy = 1, | OKbin = <>, | MyBin = [<>, OKbin]. `---- Regards, --Daniel -- Daniel Neri dne@REDACTED From Jouni.Ryno@REDACTED Mon Apr 16 21:30:43 2001 From: Jouni.Ryno@REDACTED (Jouni.Ryno@REDACTED) Date: Mon, 16 Apr 2001 22:30:43 +0300 Subject: Problem with bit syntax compilation In-Reply-To: Message from dne@mayonnaise.net (Daniel =?iso-8859-1?q?N=E9ri?=) of "16 Apr 2001 19:00:55 +0200." <87oftwlqyg.fsf@nowhere.mayonnaise.net> Message-ID: > "Jouni Ryno" writes: > > > error(Test) -> > > if > > Test == 1 -> > > Problem = 1; > > Test == 2 -> > > Problem = 2 > > end, > > Dummy = 1, > > OKbin = <>, % used only to prove, that it works > > normally > > MyBin = [<>, OKbin]. > > > > I get compilation error from erlc test.erl (eshell V5.0.2 > > Although it does look like a bug, you missed the warning: > > /tmp/test.erl:12: Warning: variable 'Problem' exported from 'if' (line 4) > I do NOT get the warning ! Hmm, do you get the warning, if you add the fallback true -> Problem = 3 % default ?? But the following does make it work, even in my real life case ! Thanks ! > So instead try: > > ,---- > | error(Test) -> > | Problem = if > | Test == 1 -> > | 1; > | Test == 2 -> > | 2 > | end, > | Dummy = 1, > | OKbin = <>, > | MyBin = [<>, OKbin]. > `---- > > > Regards, > --Daniel > > -- > Daniel Neri > dne@REDACTED Jouni Ryn? mailto://Jouni.Ryno@REDACTED/ http://www.geo.fmi.fi/~ryno/ Finnish Meteorological Institute http://www.fmi.fi/ Geophysical Research http://www.geo.fmi.fi/ P.O.BOX 503 Tel (+358)-9-19294656 FIN-00101 Helsinki FAX (+358)-9-19294603 Finland priv-GSM (+358)-50-5302903 "It's just zeros and ones, it cannot be hard" From etxuwig@REDACTED Tue Apr 17 10:13:35 2001 From: etxuwig@REDACTED (Ulf Wiger) Date: Tue, 17 Apr 2001 10:13:35 +0200 (MET DST) Subject: Delete/Create in the same transaction In-Reply-To: <402DD461F109D411977E0008C791C31203919A67@imp02mbx.one2one.co.uk> Message-ID: On Mon, 16 Apr 2001, Chandrashekhar Mullaparthi wrote: [snip] >21> mnesia:transaction(fun() -> mnesia:delete_object({bag, 1, 2}), >mnesia:write({bag, 1, 2}) end). >{atomic,ok} >22> >22> [mnesia:dirty_read({bag, X}) || X <- mnesia:dirty_all_keys(bag)]. >[[{bag,1,3}]] > >In step 21, Why is it that if I delete an object in a transaction >and write it back again in the same, the object doesn't exist in the >database?? Intuitively it seems to be a bug.... I don't know if this is your problem, but if you want to verify that the object actually gets written, you may want to do so with another transaction. The above test suffers from a race condition in that dirty operations never wait for a transaction to finish. The transaction returns when it is committed, but before all the work is done to make the data visible. Transactions are guaranteed to see the complete results of earlier transactions but dirty operations are not. /Uffe -- Ulf Wiger tfn: +46 8 719 81 95 Senior System Architect mob: +46 70 519 81 95 Strategic Product & System Management ATM Multiservice Networks Data Backbone & Optical Services Division Ericsson Telecom AB From Sean.Hinde@REDACTED Tue Apr 17 13:01:51 2001 From: Sean.Hinde@REDACTED (Sean Hinde) Date: Tue, 17 Apr 2001 12:01:51 +0100 Subject: Erlang futures Message-ID: <402DD461F109D411977E0008C791C312039F5EF3@imp02mbx.one2one.co.uk> > Hello > > I have decided to develop a Workflow engine probably with > Erlang but still not > decided ;-) > (I am also looking for OCAML and www.Open-Scheme.com) > > Erlang seem great but seem still suffer of some lacks .... > > I would like to know what improvments are planned in next R8 release > > GUI (GTK ???) I saw something about this somewhere.. > Strings (with Binaries) ??? Erlang has strings :) They are implemented as lists but I have not had any great problem with this and it makes for very easy programming. They can be stored in mnesia as binaries if you want to save the last few bytes of memory. You can also do most things with binaries already that you can do with strings. > MNESIA fast enougth for big table (up to Giga records)??? mnesia is fast enough.. There are issues with the recovery time for very large tables if the whole thing happens to crash during the log dump (which if you have a relatively low update rate is pretty unlikely - especially if you have active replicas). Which database were you thinking of using with OCAML BTW?? If you really wanted to offer a choice of database for very large users there is an ODBC interface.. > File Upload for Inets Web server ??? I don't know. Inets is fairly easy to understand and is quite modular - why not have a go? I don't know if the OTP guys are planning this.. > > (and why not some HIPE JIT parts incorporation ???) I'm sure HIPE will be coming along sometime. Erlang is pretty quick without the HIPE so I'd knock up a prototype and test/profile it before you worry unduly about this. > thanks for anwser Just give it a go. You'll probably have the thing up and running before you would have finished worrying about which language to use :-) Enjoy! - Sean NOTICE AND DISCLAIMER: This email (including attachments) is confidential. If you have received this email in error please notify the sender immediately and delete this email from your system without copying or disseminating it or placing any reliance upon its contents. We cannot accept liability for any breaches of confidence arising through use of email. Any opinions expressed in this email (including attachments) are those of the author and do not necessarily reflect our opinions. We will not accept responsibility for any commitments made by our employees outside the scope of our business. We do not warrant the accuracy or completeness of such information. From srl@REDACTED Tue Apr 17 14:43:31 2001 From: srl@REDACTED (Steve Langstaff) Date: Tue, 17 Apr 2001 13:43:31 +0100 Subject: Erlang futures Message-ID: <01C0C744.64CFFDF0.srl@terminus.ericsson.se> On 17 April 2001 12:02, Sean Hinde [SMTP:Sean.Hinde@REDACTED] wrote: > Erlang has strings :) They are implemented as lists but I have not had any > great problem with this and it makes for very easy programming. They can be > stored in mnesia as binaries if you want to save the last few bytes of > memory. I was under the (perhaps incorrect) impression that the overhead of those "last few bytes of memory" was something like 7 bytes per character in the string. It's the old "Bloatware == ease of use" trade-off that produces animated talking paperclips. Not that animated talking paperclips are _necessarily_ all bad. -- Steve L. From Sean.Hinde@REDACTED Tue Apr 17 15:18:08 2001 From: Sean.Hinde@REDACTED (Sean Hinde) Date: Tue, 17 Apr 2001 14:18:08 +0100 Subject: Erlang futures Message-ID: <402DD461F109D411977E0008C791C312039F5EFC@imp02mbx.one2one.co.uk> > > > Erlang has strings :) They are implemented as lists but I > have not had any > > great problem with this and it makes for very easy > programming. They can be > > stored in mnesia as binaries if you want to save the last > few bytes of > > memory. > > I was under the (perhaps incorrect) impression that the > overhead of those > "last few bytes of memory" was something like 7 bytes per > character in the string. Yes, you are correct. It is the last few bytes if you are storing a few strings. If you are storing War and Peace then it's the last few Megabytes :) > It's the old "Bloatware == ease of use" trade-off that > produces animated talking > paperclips. Well perhaps not quite that bad. I guess most Ericsson apps to date haven't needed to store anything like gigabytes of strings. More like a few web pages for config. But, since XML is becoming a bigger and bigger part of even core telecoms that must surely be a good driver for Eri to come up with a more optimised string type.. Here's hoping for something in R8. What we do here in the meantime is store large strings in mnesia as binaries and if we need to have string representation after a read then just convert. Higher temporary memory usage but pobably not enough to preclude the vast majority of apps. Feedback from users of our INETS based apps including XML services are that they simnply cannot believe that p[ages are not served locally. The realtime scheduler makes for an incredible user experience in these sorts of environments. Erlang may not be as outright fast as OCAML but user experience is likely to be better for much less programming effort. > Not that animated talking paperclips are _necessarily_ all bad. Grrrr - Sean NOTICE AND DISCLAIMER: This email (including attachments) is confidential. If you have received this email in error please notify the sender immediately and delete this email from your system without copying or disseminating it or placing any reliance upon its contents. We cannot accept liability for any breaches of confidence arising through use of email. Any opinions expressed in this email (including attachments) are those of the author and do not necessarily reflect our opinions. We will not accept responsibility for any commitments made by our employees outside the scope of our business. We do not warrant the accuracy or completeness of such information. From jamesh@REDACTED Tue Apr 17 15:59:48 2001 From: jamesh@REDACTED (James Hague) Date: Tue, 17 Apr 2001 08:59:48 -0500 Subject: Erlang futures Message-ID: > I was under the (perhaps incorrect) impression that the > overhead of those > "last few bytes of memory" was something like 7 bytes per > character in the string. > > It's the old "Bloatware == ease of use" trade-off that > produces animated talking > paperclips. Look at it this way: If you have 256K of active strings in an application--and that's a huge amount--then you are only wasting 1.75 megabytes of data by storing them as strings. At one time that would have sounded horrendous, but it is does not sound so bad now, not when there are libraries that commonly need 8M for undefined internal use :) There are two things that help tremendously with strings: 1. If you are doing things like fetching and parsing web pages, just handle the pages as binaries. It is just as easy to parse a binary as a string, and there is no wasted space. 2. Very rarely do you need to have tons of strings just sitting around, stored as strings. When you file away a string for the moment and don't expect to look at it for a while, call list_to_binary on it first. James From rv@REDACTED Tue Apr 17 18:58:07 2001 From: rv@REDACTED (Robert Virding) Date: Tue, 17 Apr 2001 18:58:07 +0200 Subject: Problem with bit syntax compilation In-Reply-To: Your message of "Mon, 16 Apr 2001 19:13:08 +0300." Message-ID: <200104171658.SAA04172@trana.bluetail.com> "Jouni Ryno" writes: >From the following code > >-module(test). > >error(Test) -> > if > Test == 1 -> > Problem = 1; > Test == 2 -> > Problem = 2 > end, > Dummy = 1, > OKbin = <>, % used only to prove, that it works >normally > MyBin = [<>, OKbin]. > >I get compilation error from erlc test.erl (eshell V5.0.2 > >/proj/cosima/egse/test.erl:none: internal error in v3_codegen; >crash reason: {{case_clause,{'EXIT',{function_clause, > [{v3_codegen,fetch_reg,['Problem > ... This is a compiler bug which has been fixed in the next release. The way around it is as someone already has shown. The warning should be printed AFTER the compiler error trace. Robert -- Robert Virding Tel: +46 (0)8 545 55 017 Alteon Web Systems Email: rv@REDACTED S:t Eriksgatan 44 WWW: http://www.bluetail.com/~rv SE-112 34 Stockholm, SWEDEN "Folk s?ger att jag inte bryr mig om n?gonting, men det skiter jag i". From jamesh@REDACTED Tue Apr 17 23:03:53 2001 From: jamesh@REDACTED (James Hague) Date: Tue, 17 Apr 2001 16:03:53 -0500 Subject: Erlang futures Message-ID: I wrote: > 1. If you are doing things like fetching and parsing web > pages, just handle the pages as binaries. It is just as easy > to parse a binary as a string, and there is no wasted space. On that subject, here is a binary version of lists:splitwith, which I have found handy: % Split a binary into two parts and return {list, binary}. splitwith(Pred, B) -> splitwith(Pred, B, []). splitwith(Pred, <>=B, Taken) -> case Pred(Hd) of true -> splitwith(Pred, Tail, [Hd|Taken]); false -> {lists:reverse(Taken), B} end; splitwith(Pred, <<>>, Taken) -> {lists:reverse(Taken),[]}. James From rv@REDACTED Wed Apr 18 14:55:17 2001 From: rv@REDACTED (Robert Virding) Date: Wed, 18 Apr 2001 14:55:17 +0200 Subject: Erlang futures In-Reply-To: Your message of "Tue, 17 Apr 2001 16:03:53 CDT." Message-ID: <200104181255.OAA07709@trana.bluetail.com> James Hague writes: >On that subject, here is a binary version of lists:splitwith, which I have >found handy: > >% Split a binary into two parts and return {list, binary}. > >splitwith(Pred, B) -> > splitwith(Pred, B, []). >splitwith(Pred, <>=B, Taken) -> > case Pred(Hd) of > true -> splitwith(Pred, Tail, [Hd|Taken]); > false -> {lists:reverse(Taken), B} > end; >splitwith(Pred, <<>>, Taken) -> > {lists:reverse(Taken),[]}. To be truly a binary version it should return two binaries not a list and binary. I think the easiest and most efficient way would be: splitwith(Pred, B) -> split_binary(B, splitwith(Pred, B, 0)). splitwith(Pred, <>, I) -> case Pred(H) of true -> splitwith(Pred, T, I+1); false -> I end; splitwith(Pred, <<>>, I) -> I. For example this is also what is wrong with ets:filter/3, it returns a list but it should really return a filtered ets table. Robert -- Robert Virding Tel: +46 (0)8 545 55 017 Alteon Web Systems Email: rv@REDACTED S:t Eriksgatan 44 WWW: http://www.bluetail.com/~rv SE-112 34 Stockholm, SWEDEN "Folk s?ger att jag inte bryr mig om n?gonting, men det skiter jag i". From jamesh@REDACTED Wed Apr 18 15:44:02 2001 From: jamesh@REDACTED (James Hague) Date: Wed, 18 Apr 2001 08:44:02 -0500 Subject: Erlang futures Message-ID: > To be truly a binary version it should return two binaries not a list > and binary. I think the easiest and most efficient way would be: True. I've found that when parsing a binary I usually need to take the parsed bit at the end and turn it into a list anyway. But maybe not in all cases, so it would be better to leave the conversion to the caller. BTW, the last lines of my original code should have been: splitwith(Pred, <<>>, Taken) -> {lists:reverse(Taken),<<>>}. James From richardc@REDACTED Wed Apr 18 19:23:45 2001 From: richardc@REDACTED (Richard Carlsson) Date: Wed, 18 Apr 2001 19:23:45 +0200 (MET DST) Subject: Core Erlang minor update In-Reply-To: Message-ID: A small update of the Core Erlang specification has been made. See http://www.csd.uu.se/projects/hipe/cerl/ for details. /Richard Carlsson Richard Carlsson (richardc@REDACTED) (This space intentionally left blank.) E-mail: Richard.Carlsson@REDACTED WWW: http://www.csd.uu.se/~richardc/ From etxuwig@REDACTED Wed Apr 18 22:01:14 2001 From: etxuwig@REDACTED (Ulf Wiger) Date: Wed, 18 Apr 2001 22:01:14 +0200 (MET DST) Subject: Erlang futures In-Reply-To: <200104181255.OAA07709@trana.bluetail.com> Message-ID: On Wed, 18 Apr 2001, Robert Virding wrote: >For example this is also what is wrong with ets:filter/3, it returns a >list but it should really return a filtered ets table. Hmm, except that ets tables are not garbage collected, and there's an upper limit on the number of ets tables that can be created. Such a function would not be side effect free, in the sense that the caller would then explicitly have to deallocate the automatically created ets table... unless: ets:filter(Tab, F, A, ResultTable) -> true. where the result of the filter operation is found in ResultTable But the basic idea of ets:filter/3, ets:foldl/3 et al was that they would be analogous to the corresponding lists functions. The most important reason for putting them in was that it would be easy to modify code that first called ets:tab2list(Tab) and then iterated over the result -- this being bad because it causes trouble for the Erlang memory handling. /Uffe -- Ulf Wiger tfn: +46 8 719 81 95 Senior System Architect mob: +46 70 519 81 95 Strategic Product & System Management ATM Multiservice Networks Data Backbone & Optical Services Division Ericsson Telecom AB From ahltorp@REDACTED Wed Apr 18 22:36:11 2001 From: ahltorp@REDACTED (Magnus Ahltorp) Date: 18 Apr 2001 22:36:11 +0200 Subject: Erlang futures In-Reply-To: Ulf Wiger's message of "Wed, 18 Apr 2001 22:01:14 +0200 (MET DST)" Message-ID: How can I get a list of the IP addresses on the machine the node is running on? There are two drivers named udp_inet and tcp_inet that seem to return that when INET_REQ_IFGET is requested. Is this the correct way? In that case, how do I call this driver? If not, how do I get the list? /Magnus From radhia@REDACTED Thu Apr 19 00:02:42 2001 From: radhia@REDACTED (radhia@REDACTED) Date: Thu, 19 Apr 2001 00:02:42 +0200 (CEST) Subject: SAS'01 Call For Participation Message-ID: <200104182202.AAA29145@albatros.polytechnique.fr> PROGRAMME AND CALL FOR PARTICIPATION SAS'01 Eigth International Static Analysis Symposium La Sorbonne, Paris, 16-18 July, 2001 http://www.ens.fr/sas01/ Registration information is available at the bottom of this announcement. Electronic registration is also available on the SAS'01 website. The hotels reservations are to be done by --22 April 2001--. PROGRAMME MONDAY JULY 16 8:00-9:00 Registration 9:00-10:00 Invited Talk Program Analysis for Multi-threaded Programs Martin C. Rinard (Massachusetts Institute of Technology) 10:00-10:30 Break 10:30-12:00 Program transformation Communication and Parallelism Introduction and Elimination by Static Formal Transformations Miquel Bertran, Francesco Babot and August Climent Miquel Nicolau Using Slicing to Identify Duplication in Source Code Raghavan Komondoor and Susan Horwitz Soft Scheduling for Hardware Richard Sharp and Alan Mycroft 12:00-13:30 Lunch 13:30-15:00 Strictness and termination Effective Strictness Analysis with HORN Constraints Kevin Glynn, Peter J. Stuckey, and Martin Sulzmann Applying Static Analysis Techniques for Inferring Termination Conditions of Logic Programs Fred Mesnard and Ulrich Neumerkel An Abstract Analysis of the Probabilistic Termination of Programs David Monniaux 15:00-15:30 Break 15:30-17:00 Semantics abstraction Watchpoint Semantics: A Tool for Compositional and Focussed Static Analyses Fausto Spoto Logic and constraint programming Parameterizing a Groundness Analysis of Logic Programs Lunjin Lu Finite-Tree Analysis for Constraint Logic-Based Languages Roberto Bagnara, Roberta Gori, Patricia M. Hill and Enea Zaffanella 20:00 Reception TUESDAY, JULY 17 9:00-10:00 Invited Talk Applications of Extended Static Checking K. Rustan M. Leino (Compaq SRC) 10:00-10:30 Break 10:30-12:00 Data structures Cleanness Checking of String Manipulations in C Programs via Integer Analysis Nurit Dor, Michael Rodeh and Mooly Sagiv Solving Regular Tree Grammar Based Constraints Yanhong A. Liu, Ning Li and Scott D. Stoller Static analyses of floating-point operations ?ric Goubault 12:00-13:30 Lunch 13:30-15:30 Pointer analysis Estimating the Impact of Scalable Pointer Analysis on Optimization Manuvir Das, Ben Liblit, Manuel Fahndrich and Jakob Rehof Efficient Computation of Parameterized Pointer Information for Interprocedural Analyses Donglin Liang and Mary Jean Harrold Model Checking Parametric Optimization of Open Real-Time Systems Farn Wang and Hsu-Chun Yen Embedding Chaos Natalia Sidorova and Martin Steffen 15:30-16:00 Break 16:00-17:00 Abstract Model Checking Analyzing Fair Parametric Extended Automata A. Annichini, A. Bouajjani, Y. Lakhnech, and M. Sighireanu Incompleteness, Counterexamples and Refinements in Abstract Model-Checking Roberto Giacobazzi and Elisa Quintarelli 20:00 Reception WEDNESDAY, JULY 18 9:00-10:00 Invited Talk Language-based Security: What's needed and Why Fred Schneider (Cornell University) 10:00-10:30 Break 10:30-12:00 Mobility A Behavioral Module System for the Pi-Calculus Sriram K. Rajamani and Jakob Rehof An Abstract Interpretation Framework for Analysing Mobile Ambients Francesca Levi and Sergio Maffeis Abstract Interpretation-Based Static Analysis of Mobile Ambients J?r?me Feret 12:00-13:30 Lunch 13:30-15:00 Invited Session on Security ... David Wagner (University of california, Berkeley) A type and Effect Analysis of Security Protocols (joint work with Alan jeffrey) Andrew Gordon (Microsoft Research, Cambridge) 15:00-15:30 Break 15:30-17:00 Invited Session on Security (Cont'd) Abstracting Cryptographic Protocols by Prolog Rules Bruno Blanchet (Inria Rocquencourt) Security-Typed languages Andrew Myers (Cornell University) ------------------------- REGISTRATION FORM --------------------- Send your registration form with payment to: SAS'01 c/o Evelyne Rayssac LIX Ecole Polytechnique 91128 Palaiseau cedex France Phone: +33 1 69 33 45 95 Fax: +33 1 69 33 30 14 email: sas01@REDACTED Electronic registration is also available at URL: http://www.ens.fr/sas01/ Name:............................................................ Affiliation:..................................................... Address:......................................................... ......................................................... ......................................................... Phone:........................................................... Fax:............................................................. E-mail:.......................................................... Dietary requirements:............................................ Do you need an Internet connection for a laptop from your hotel?: ......................................................... Payment: enclosed check ___ enclosed order form (for french institutions only) ___ bank tranfer (enclose a copy of the transfer order) ___ Registration fees: (in Euros or French Francs) by June, 15 2001: 500 ? ___ or 3,300 FF___ 365 ? ___ or 2,400 FF___ (Full-time Student) after June, 15 2001: 550 ? ___ or 3,600 FF___ 415 ? ___ or 2,700 FF___ (Full-time Student) Registration includes the proceedings, reception, lunches and coffee breaks. ---------------------------- PAYMENT ----------------------------- Choose one of the three following options for the payment of the registration fees. Make sure that the bank and change costs are at your charge. - Bank transfer to: Bank: Tresorerie Generale des Hauts-de-Seine 167, avenue Joliot Curie 92013 Nanterre Cedex, France (State your name and the reference: SAS'01) Domiciliation: RGFIN PARIS NANTERRE Account Number: 40071-9200-00003000310-90 Account Name: CNRS IDF OUEST-NORD (A copy of the transfer order is to be sent with the registration form) - Make checks payable to: Agent comptable secondaire CNRS IDF Ouest & Nord (to be sent with the registration form) - Purchase Order Form (for french institutions only) to: LIX-CNRS (to be sent with the registration form) ------------------------ HOTEL RESERVATION ---------------------- Hotel and travel information, including detailed information on room reservations at any of a number of hotels near the Ecole Normale Supe'rieure, is available at the URL http://www.ens.fr/sas01/ We have arranged rooms for conference attendees at the hotels listed below. To make a reservation, send a fax to the hotel mentioning your credit card number and the reference "SAS'01, La Sorbonne". Please note that all of the reservations are to be done by ---22 April 2001---. (**) Hotel Pierre Nicole 39 rue Pierre Nicole Paris 75005 Phone: +33 (0)1 43 54 76 86 Fax: +33 (0)1 43 54 22 45 Single: 390F, Double: 440F, Breakfast: 40F hotelpierre-nicole@REDACTED (***) Observatoire du Luxembourg 107, Boulevard Saint-Michel Paris 75005 Phone: +33 (0)1 46 34 10 12 Fax: +33 (0)1 46 33 73 86 Single: 600F, Double: 700F, Breakfast: included http://Paris.HotelGuide.net/data/h100051.htm (***) Hotel des Jardins de Luxembourg 5 Impasse Royal-Collard Paris 75005 Phone: +33 (0)1 40 46 08 88 Fax: +33 (0)1 40 46 02 28 Single or Double: 825F, Breakfast: 55F http://Paris.HotelGuide.net/data/h100049.htm (***) Hotel Luxembourg luxhotel@REDACTED 4, rue de Vaugirard 75006 Paris Phone: +33 (0)1 43 25 35 90 Fax: +33 (0)1 43 25 17 18 Single: 800F / Double: 910F, Breakfast: included http://www.hotel-luxembourg.com/index.html (***) Hotel du Pantheon Place du Pantheon 75005 Paris Phone: +33 (0)1 43 54 32 95 Fax: +33 (0)1 43 26 64 65 Single or Double: 1100F, Triple: 1300F, Breakfast: 55F http://www.france-hotel-guide.com/h75005pantheon.htm Students rooms at the ENS-Cachan Campus (For students registrating at SAS'01 & CAV'01) 61 av. du Pdt Wilson 94235 Cachan RER-B: Bagneux (45min) Single: 120F (contact person: duflot@REDACTED) http://www.lsv.ens-cachan.fr/cav01/ ----------------------------------------------------------------- From raimo@REDACTED Thu Apr 19 09:31:22 2001 From: raimo@REDACTED (Raimo Niskanen) Date: Thu, 19 Apr 2001 09:31:22 +0200 Subject: Erlang futures References: Message-ID: <3ADE944A.9F45F80B@erix.ericsson.se> You can try the following undocomented (and unsupported, but it is not very likely that they will change much in the future :-) functions in lib/kernel/src/inet.erl: 18> inet:getiflist(). {ok,["lo0","hme0"]} 19> inet:ifget("lo0", [addr, broadaddr, dstaddr, mtu, netmask, flags, hwaddr]). {ok,[{addr,{127,0,0,1}}, {netmask,{255,0,0,0}}, {flags,[up,loopback,running,multicast]}]} 20> inet:ifget("hme0", [addr, broadaddr, dstaddr, mtu, netmask, flags, hwaddr]). {ok,[{addr,{134,138,177,99}}, {broadaddr,{134,138,179,255}}, {netmask,{255,255,252,0}}, {flags,[up,broadcast,running,multicast]}]} "Use the Source, Luke. Let it guide Your hands." / Raimo Niskanen, Ericsson UAB, Erlang/OTP. Magnus Ahltorp wrote: > > How can I get a list of the IP addresses on the machine the node is > running on? There are two drivers named udp_inet and tcp_inet that > seem to return that when INET_REQ_IFGET is requested. > > Is this the correct way? In that case, how do I call this driver? If > not, how do I get the list? > > /Magnus From klacke@REDACTED Thu Apr 19 09:45:11 2001 From: klacke@REDACTED (Klacke) Date: Thu, 19 Apr 2001 09:45:11 +0200 Subject: Erlang futures In-Reply-To: ; from ahltorp@nada.kth.se on Wed, Apr 18, 2001 at 10:36:11PM +0200 References: Message-ID: <20010419094511.A6211@bluetail.com> On Wed, Apr 18, 2001 at 10:36:11PM +0200, Magnus Ahltorp wrote: > How can I get a list of the IP addresses on the machine the node is > running on? There are two drivers named udp_inet and tcp_inet that > seem to return that when INET_REQ_IFGET is requested. > > Is this the correct way? In that case, how do I call this driver? If > not, how do I get the list? > -module(a). -author('klacke@REDACTED'). -include_lib("kernel/include/inet.hrl"). -compile(export_all). ips() -> {ok, Name} = inet:gethostname(), {ok, HE} = inet:gethostbyname(Name), HE#hostent.h_addr_list. This of cource only returns info from the configured name service. The call inet:getif() will query the driver the way you want and return a list of {IP, Bcast, Mask} tuples /klacke -- Claes Wikstrom -- Caps lock is nowhere and Alteon WebSystems -- everything is under control http://www.bluetail.com/~klacke -- From rv@REDACTED Thu Apr 19 12:43:24 2001 From: rv@REDACTED (Robert Virding) Date: Thu, 19 Apr 2001 12:43:24 +0200 Subject: Core Erlang minor update In-Reply-To: Your message of "Wed, 18 Apr 2001 19:23:45 +0200." Message-ID: <200104191043.MAA11438@trana.bluetail.com> Richard Carlsson writes: > >A small update of the Core Erlang specification has been made. See > > http://www.csd.uu.se/projects/hipe/cerl/ > >for details. I must make one small point here. The current BEAM compiler already uses Core internally. It is, however, not quite the same Core as richard has documented although they are (slowly) converging. One major difference is the that the internal representations are different even though the textual forms are very alike. If you are interested in looking at it check out the core_XXX modules. To see the how Erlang is converted to Core try the following compiler flags: dcore - direct Core representation of the Erlang code dcopt - Core code after optimisation You can directly write Core modules but it is not really supported. There is no benefit in doing it, although in some respects the Core language has some nice features, and some features which make it a pain to use directly. Robert -- Robert Virding Tel: +46 (0)8 545 55 017 Alteon Web Systems Email: rv@REDACTED S:t Eriksgatan 44 WWW: http://www.bluetail.com/~rv SE-112 34 Stockholm, SWEDEN "Folk s?ger att jag inte bryr mig om n?gonting, men det skiter jag i". From rv@REDACTED Thu Apr 19 12:49:25 2001 From: rv@REDACTED (Robert Virding) Date: Thu, 19 Apr 2001 12:49:25 +0200 Subject: Core Erlang minor update In-Reply-To: Your message of "Thu, 19 Apr 2001 12:43:24 +0200." <200104191043.MAA11438@trana.bluetail.com> Message-ID: <200104191049.MAA11482@trana.bluetail.com> Robert Virding writes: >If you are interested in looking at it check out the core_XXX >modules. To see the how Erlang is converted to Core try the following >compiler flags: > >dcore - direct Core representation of the Erlang code >dcopt - Core code after optimisation I forgot to mention that the first one produces a .core file and the second one a .coreopt file. These contain the converted/optimised code. Robert From willem@REDACTED Thu Apr 19 14:02:43 2001 From: willem@REDACTED (Willem Broekema) Date: Thu, 19 Apr 2001 14:02:43 +0200 Subject: loop exit question Message-ID: <3ADED3E3.2020103@imeme.net> If a function should loop forever while remembering some state, most examples I have seen use: loop(State) -> receive stop -> stopped_upon_request; Other -> ..., loop(State) end. Now, if the State is not changed in any of the 'reveiced' branches, is there a reason not to move the final 'loop()' command to the end, and use 'exit()' for breaking out of the loop, like in the following? loop2(State) -> receive stop -> exit(self(), stopped_upon_request); Other -> ... end, loop(State). TIA - Willem From klacke@REDACTED Thu Apr 19 14:11:09 2001 From: klacke@REDACTED (Klacke) Date: Thu, 19 Apr 2001 14:11:09 +0200 Subject: loop exit question In-Reply-To: <3ADED3E3.2020103@imeme.net>; from willem@imeme.net on Thu, Apr 19, 2001 at 02:02:43PM +0200 References: <3ADED3E3.2020103@imeme.net> Message-ID: <20010419141109.B30152@bluetail.com> On Thu, Apr 19, 2001 at 02:02:43PM +0200, Willem Broekema wrote: > If a function should loop forever while remembering some > state, most examples I have seen use: > > loop(State) -> > receive > stop -> > stopped_upon_request; > Other -> > ..., > loop(State) > end. > > Now, if the State is not changed in any of the 'reveiced' > branches, is there a reason not to move the final 'loop()' > command to the end, and use 'exit()' for breaking out of > the loop, like in the following? > > loop2(State) -> > receive > stop -> > exit(self(), stopped_upon_request); > Other -> > ... > end, > loop(State). > > No, that's the way to do it. /klacke -- Claes Wikstrom -- Caps lock is nowhere and Alteon WebSystems -- everything is under control http://www.bluetail.com/~klacke -- From srl@REDACTED Thu Apr 19 14:21:57 2001 From: srl@REDACTED (Steve Langstaff) Date: Thu, 19 Apr 2001 13:21:57 +0100 Subject: loop exit question Message-ID: <01C0C8D3.B5FB16C0.srl@terminus.ericsson.se> On 19 April 2001 13:11, Klacke [SMTP:klacke@REDACTED] wrote: > On Thu, Apr 19, 2001 at 02:02:43PM +0200, Willem Broekema wrote: > > If a function should loop forever while remembering some > > state, most examples I have seen use: > > > > loop(State) -> > > receive > > stop -> > > stopped_upon_request; > > Other -> > > ..., > > loop(State) > > end. > > > > Now, if the State is not changed in any of the 'reveiced' > > branches, is there a reason not to move the final 'loop()' > > command to the end, and use 'exit()' for breaking out of > > the loop, like in the following? > > > > loop2(State) -> > > receive > > stop -> > > exit(self(), stopped_upon_request); > > Other -> > > ... > > end, > > loop(State). > > > > > > > No, that's the way to do it. Doesn't the action of exit change, depending on whether the process is trapping exits? A light perusal of "Concurrent Programming in ERLANG" seems to suggest that exit will/may cause a process to terminate, whereas one might just want to drop out of a loop. Or have I misunderstood the original question? -- Steve L. From gunilla@REDACTED Thu Apr 19 14:27:26 2001 From: gunilla@REDACTED (Gunilla Hugosson) Date: Thu, 19 Apr 2001 14:27:26 +0200 Subject: loop exit question References: <3ADED3E3.2020103@imeme.net> <20010419141109.B30152@bluetail.com> Message-ID: <3ADED9AD.84E72382@erix.ericsson.se> Klacke wrote: > > On Thu, Apr 19, 2001 at 02:02:43PM +0200, Willem Broekema wrote: > > If a function should loop forever while remembering some > > state, most examples I have seen use: > > > > loop(State) -> > > receive > > stop -> > > stopped_upon_request; > > Other -> > > ..., > > loop(State) > > end. > > > > Now, if the State is not changed in any of the 'reveiced' > > branches, is there a reason not to move the final 'loop()' > > command to the end, and use 'exit()' for breaking out of > > the loop, like in the following? > > > > loop2(State) -> > > receive > > stop -> > > exit(self(), stopped_upon_request); > > Other -> > > ... > > end, > > loop(State). > > > > > > No, that's the way to do it. > I disagree. You might be saved the effort of having to type "loop(State)" more than once, but you lose readability and maintainability. I can't count the number of times I have debugged code behaving strangely and found constructs like this: loop(State) -> stop -> exit(stop); ... Pattern -> ..., loop(State); ... Other -> ignore end, loop(State). / Gunilla From rv@REDACTED Thu Apr 19 14:53:19 2001 From: rv@REDACTED (Robert Virding) Date: Thu, 19 Apr 2001 14:53:19 +0200 Subject: loop exit question In-Reply-To: Your message of "Thu, 19 Apr 2001 14:02:43 +0200." <3ADED3E3.2020103@imeme.net> Message-ID: <200104191253.OAA11841@trana.bluetail.com> Willem Broekema writes: > ... >Now, if the State is not changed in any of the 'reveiced' >branches, is there a reason not to move the final 'loop()' >command to the end, and use 'exit()' for breaking out of >the loop, like in the following? > >loop2(State) -> > receive > stop -> > exit(self(), stopped_upon_request); > Other -> > ... > end, > loop(State). NOTE!!!! Exit/2 sends an exit signal to a process, so exit(self(),xxx) sends an exit signal to itself. This is *NOT* the same as calling exit(xxx)!!! The exit/2 signal can be trapped while exit/1 cannot, while exit/1 can be caught (in a catch) while exit/2 signals cannot. You have to be careful to choose the "right" one. This difference is intentional. Robert From willem@REDACTED Thu Apr 19 15:39:09 2001 From: willem@REDACTED (Willem Broekema) Date: Thu, 19 Apr 2001 15:39:09 +0200 Subject: loop exit question References: <01C0C8D3.B5FB16C0.srl@terminus.ericsson.se> Message-ID: <3ADEEA7D.60506@imeme.net> Steve Langstaff wrote: > Doesn't the action of exit change, depending on whether the process > is trapping exits? > > A light perusal of "Concurrent Programming in ERLANG" > seems to suggest that exit will/may cause a process to terminate, whereas > one might just want to drop out of a loop. Or have I misunderstood the > original question? Indeed, my question was not clear about that. In my situation, the loop function is a tcp connection listener, receiving either 'stop', or '{tcp, Sock, Data}'. The latter packets are parsed and send to a wrapper, so the loop is always called with the same arguments: connListener(ConnWrapperPid, Sock) As soon as 'stop' is received, the process may terminate, as this is the only function running on the process (it was spawn_link()-ed). I had not paid attention to exit/1 versus exit/2, so thanks for pointing out the important difference. I'll look into it. Thanks for the fast responses, and the great language... so few lines, so much functionality! :-) - Willem From etxuwig@REDACTED Thu Apr 19 15:40:06 2001 From: etxuwig@REDACTED (Ulf Wiger) Date: Thu, 19 Apr 2001 15:40:06 +0200 (MET DST) Subject: loop exit question In-Reply-To: <3ADED3E3.2020103@imeme.net> Message-ID: On Thu, 19 Apr 2001, Willem Broekema wrote: >Now, if the State is not changed in any of the 'reveiced' >branches, is there a reason not to move the final 'loop()' >command to the end, and use 'exit()' for breaking out of >the loop, like in the following? > >loop2(State) -> > receive > stop -> > exit(self(), stopped_upon_request); > Other -> > ... > end, > loop(State). How you want to terminate your process depends in part on how you're implementing it (plain erlang, gen_server, 'sys' compliant plain erlang, ...), and in part on the context in which it is executing. I would write the loop like this: loop2(State) -> receive stop -> terminate(normal, State); Other -> NewState = handle_message(Other, State), loop2(NewState) end. terminate(Reason, State) -> exit(Reason). %% note: exit/1 handle_message(Msg, State) -> ... I will give a few examples of context issues to consider: - if you're using SASL, and the process was started with e.g. proc_lib:spawn_link/3 (as it should), exit(stopped_upon_request) would be interpreted as an abnormal exit, and would lead to a crash report. exit(normal) would not. - If the process is supervised, and is started as a transient process, any exit reason other than normal would cause the supervisor to restart the process. - If the process is supervised and trapping EXITs, it would have to trap the following message: receive {'EXIT', Parent, shutdown} -> %% assume Parent is bound terminate(shutdown, State); ... end. This in order to comply with the OTP shutdown protocol. Of course, if you use an OTP behaviour, like gen_server, you get much of this for free. /Uffe -- Ulf Wiger tfn: +46 8 719 81 95 Senior System Architect mob: +46 70 519 81 95 Strategic Product & System Management ATM Multiservice Networks Data Backbone & Optical Services Division Ericsson Telecom AB From rv@REDACTED Thu Apr 19 18:09:18 2001 From: rv@REDACTED (Robert Virding) Date: Thu, 19 Apr 2001 18:09:18 +0200 Subject: Erlang futures In-Reply-To: Your message of "Wed, 18 Apr 2001 22:01:14 +0200." Message-ID: <200104191609.SAA12460@trana.bluetail.com> Ulf Wiger writes: >But the basic idea of ets:filter/3, ets:foldl/3 et al was that they >would be analogous to the corresponding lists functions. The >most important reason for putting them in was that it would be >easy to modify code that first called ets:tab2list(Tab) and >then iterated over the result -- this being bad because it causes >trouble for the Erlang memory handling. Yes, but a map/fold/filter is really a function -> not a function -> . So in lists it goes [X] -> [X] and in ets it should go ets(X) -> ets(X) NOT ets(X) -> [X]. I quite understand why they are there, they should be there. My comment was just that they should "return" an ets table not a list. Considering ets itself is destructive it would be ok for ets:map/fold/filter to also be destructive and modify the original table. To late to change, but it is a pity. Robert From executiveforum@REDACTED Thu Apr 19 17:44:51 2001 From: executiveforum@REDACTED (=?ISO-8859-1?B?VGFsYXJmb3J1bSBpIFNrYW5kaW5hdmllbiBBQg==?=) Date: Thu, 19 Apr 2001 15:44:51 UT Subject: =?ISO-8859-1?B?TfZ0ZXIgRHUgQmlsbCBDbGludG9uIGRlbiAxNSBtYWogaSBTdG9ja2hvbG0gPw==?= Message-ID: Hej! Som du s?kert k?nner till kommer Bill Clinton till Sverige och Stockholm den 15 maj f?r att medverka p? ett ledarskapsseminiarium med temat: "Business&Politics ? Corporate Citizenship." Efter seminariet h?lls en stor presidentbankett p? Grand Hotel med underh?llning, storbandsjazz mm. Vem vet, kanske vi f?r h?ra en extra saxofonist? Det finns fortfarande m?jlighet att reservera biljetter till detta evenemang, b?de till seminariet och till banketten. Talare vid seminariet ?r f?rutom Bill Clinton sj?lv, Carl Bildt, S?ren Gyll, Michael Treschow samt G?ran Lindahl, Hans Dalborg, Viveca Ax:son Johnson och Jan Carendi. Missa inte tillf?llet att lyssna p? Bill Clinton! Just nu erbjuder vid Dig och Ditt f?retag rabatt: Om ni vill g? fyra personer betalar ni endast f?r tre. Vill Du utnytta detta erbjudande s? ring oss p? telefon 08-545 535 25 s? hj?lper vi dig med din anm?lan. Mer information om evenemanget samt m?jlighet att boka plats finner du p? www.talarforum.se/clinton Med v?nlig h?lsning Talarforum AB From vances@REDACTED Fri Apr 20 00:43:44 2001 From: vances@REDACTED (Vance Shipley) Date: Thu, 19 Apr 2001 18:43:44 -0400 Subject: Hierarchical Distribution Message-ID: Folks, I came across Ericsson's 'Cello' product recently and found the description of it's "execution platform" quite interesting. I am left wondering if it might be Erlang/OTP based. In any event I'm interested in how Erlang/OTP could be used to implement the distribution as it is described in the white paper: http://www.ericsson.com/review/1999_02/files/1999021.pdf The hardware is made up of a number (1-50) of 19" rack mount chassis which they refer to as 'subracks'. In each subrack there is a 'main processor' (MP) board as well as a number (1-25) of 'device boards'. The device boards contain 'board processors' (BP) and may also contain 'subordinate processors' (SP). The logical topology uses a 'main processor cluster' (MPC). The BPs are hierarchically under a single MP while the SPs are hierarchically under a single BP. +----------------------------+ | MPC +----+ | +----+ +----+ | | MP | |------| BP | -----| SP | | +----+ /| +----+ / +----+ | +----+ +----+ / | +----+/ +----+ | | MP | | MP |<--|------| BP |-------| SP | | +----+ +----+ \ | +----+\ +----+ | \| +----+ \ +----+ | +----+ |------| BP | -----| SP | | | MP | | +----+ +----+ | +----+ | +----------------------------+ So in distribution terms BPs only know about a single MP, and possibly some SPs. Each MP knows about all other MPs but only knows about it's own BPs, and I guess not any SPs. Can the standard net_kernel be configured in this way? It seems to me that this architecture would improve the scalability of the system as it has been said that an Erlang/OTP network can't grow too large because each node knows about each other node. It would also cut down on the traffic. If SPs were in telephones for instance they might come and go quite often. There would be no sense in telling other nodes (MPs, other BPs, other SPs) about them. -Vance From mbj@REDACTED Fri Apr 20 09:21:24 2001 From: mbj@REDACTED (Martin Bjorklund) Date: Fri, 20 Apr 2001 09:21:24 +0200 Subject: Hierarchical Distribution In-Reply-To: Your message of "Thu, 19 Apr 2001 18:43:44 -0400" References: Message-ID: <20010420092124W.mbj@bluetail.com> "Vance Shipley" wrote: > The logical topology uses a 'main processor cluster' (MPC). The > BPs are hierarchically under a single MP while the SPs are hierarchically > under a single BP. > > +----------------------------+ > | MPC +----+ | +----+ +----+ > | | MP | |------| BP | -----| SP | > | +----+ /| +----+ / +----+ > | +----+ +----+ / | +----+/ +----+ > | | MP | | MP |<--|------| BP |-------| SP | > | +----+ +----+ \ | +----+\ +----+ > | \| +----+ \ +----+ > | +----+ |------| BP | -----| SP | > | | MP | | +----+ +----+ > | +----+ | > +----------------------------+ > > So in distribution terms BPs only know about a single MP, and possibly > some SPs. Each MP knows about all other MPs but only knows about it's > own BPs, and I guess not any SPs. > > Can the standard net_kernel be configured in this way? This is how our system works at bluetail, in order to make it scalable. We have a main cluster (which we call master cluster) of 2-4 master nodes. This is normal erlang distribution; fully connected net, the mnesia tables are replicated on these nodes etc. Then we have up to 252 slave nodes (like the BPs in the pic above). They are so called 'hidden nodes'. The reason for doing this is scalability; we really don't want a fully connected net of 256 nodes with mnesia actively replicating everything... Now, the net_kernel in R7B cannot handle 'hidden nodes'. It does handle 'hidden connections' though. A hidden node is a distributed erlang node which won't show up in the nodes() list. But it's got a node name, and you can use normal erlang rpc and so on. Hidden nodes is a very powerful concept (we also use them for several other things), which I think should be part of the standard release. I'll include the modified files below. Start a hidden node like this: erl -sname xxx -kernel hidden true A hidden node only accepts hidden connection attempts. This means that a hidden node can connect to another hidden node, like SP to BP in the picture above. Normally, the hidden node connects to the normal node, although a normal node can do a 'hidden connect' in order to connect to a hidden node. Try this by starting a hidden node, and a normal node. Do a net:ping() from the hidden node to the normal node. Do net_kernel:i() to print the connections in both nodes. Also check erlang:nodes(). /martin -------------- next part -------------- %% ``The contents of this file are subject to the Erlang Public License, %% Version 1.1, (the "License"); you may not use this file except in %% compliance with the License. You should have received a copy of the %% Erlang Public License along with this software. If not, it can be %% retrieved via the world wide web at http://www.erlang.org/. %% %% Software distributed under the License is distributed on an "AS IS" %% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See %% the License for the specific language governing rights and limitations %% under the License. %% %% The Initial Developer of the Original Code is Ericsson Utvecklings AB. %% Portions created by Ericsson are Copyright 1999, Ericsson Utvecklings %% AB. All Rights Reserved.'' %% %% $Id: dist_util.erl,v 1.6 2000/11/07 13:56:13 erlang Exp $ %% %%%---------------------------------------------------------------------- %%% Purpose : The handshake of a streamed distribution connection %%% in a separate file to make it usable for other %%% distribution protocols. %%%---------------------------------------------------------------------- -module(dist_util). %%-compile(export_all). -export([handshake_we_started/1, handshake_other_started/1, start_timer/1, setup_timer/2, reset_timer/1, cancel_timer/1, shutdown/2]). -import(error_logger,[error_msg/2]). -include("dist_util.hrl"). -include("dist.hrl"). -define(to_port(FSend, Socket, Data), case FSend(Socket, Data) of {error, closed} -> self() ! {tcp_closed, Socket}, {error, closed}; R -> R end). -define(int16(X), [((X) bsr 8) band 16#ff, (X) band 16#ff]). -define(int32(X), [((X) bsr 24) band 16#ff, ((X) bsr 16) band 16#ff, ((X) bsr 8) band 16#ff, (X) band 16#ff]). -define(i16(X1,X0), (?u16(X1,X0) - (if (X1) > 127 -> 16#10000; true -> 0 end))). -define(u16(X1,X0), (((X1) bsl 8) bor (X0))). -define(u32(X3,X2,X1,X0), (((X3) bsl 24) bor ((X2) bsl 16) bor ((X1) bsl 8) bor (X0))). -record(tick, {read = 0, write = 0, tick = 0, ticked = 0 }). handshake_other_started(HSData) -> {Flags,Node,Version} = recv_name(HSData), NewHSData = HSData#hs_data{other_flags = Flags, other_version = Version, other_node = Node, other_started = true}, is_allowed(NewHSData), mark_pending(NewHSData). %% %% check if connecting node is allowed to connect %% with allow-node-scheme %% is_allowed(#hs_data{other_node = Node, allowed = Allowed} = HSData) -> case lists:member(Node, Allowed) of false when Allowed /= [] -> send_status(HSData, not_allowed), error_msg("** Connection attempt from " "disallowed node ~w ** ~n", [Node]), ?shutdown(Node); _ -> true end. %% No nodedown will be sent if we fail before this process has %% succeeded to mark the node as pending ! %% mark_pending(#hs_data{other_node = Node} = HSData) -> ?debug({"MD5 connection from ~p (V~p)~n", [Node, HSData#hs_data.other_version]}), mark_pending_2(HSData), {MyCookie,HisCookie} = get_cookies(Node), ChallengeA = gen_challenge(), send_challenge(HSData, ChallengeA), reset_timer(HSData#hs_data.timer), ChallengeB = recv_challenge_reply(HSData, ChallengeA, MyCookie), send_challenge_ack(HSData, gen_digest(ChallengeB, HisCookie)), ?debug({dist_util, self(), accept_connection, Node}), connection(HSData). mark_pending_2(#hs_data{kernel_pid = Kernel, other_node = Node, this_node = MyNode} = HSData) -> case do_mark_pending(Kernel,Node, (HSData#hs_data.f_address)(HSData#hs_data.socket, Node), HSData#hs_data.other_flags) of ok -> send_status(HSData, ok), reset_timer(HSData#hs_data.timer), true; pending -> ?trace("Simultaneous connect (md5), " "i am ~p, she is ~p~n", [MyNode, Node]), if MyNode > Node -> send_status(HSData, nok), ?shutdown(Node); true -> send_status(HSData, ok_simultaneous), do_remark_pending(Kernel, Node), reset_timer(HSData#hs_data.timer), true end; up_pending -> %% Check if connection is still alive, no %% implies that the connection is no longer pending %% due to simultaneous connect do_alive(HSData), %% This can happen if the other node goes down, %% and goes up again and contact us before we have %% detected that the socket was closed. wait_pending(Kernel), reset_timer(HSData#hs_data.timer), true; already_pending -> %% FIXME: is this a case ? ?debug({dist_util,self(),mark_pending2, already_pending,Node}), ?shutdown(Node) end. %% %% Marking pending and negotiating away %% simultaneous connection problems %% wait_pending(Kernel) -> receive {Kernel, pending} -> ?trace("wait_pending returned for pid ~p.~n", [self()]), ok end. do_alive(#hs_data{other_node = Node} = HSData) -> send_status(HSData, alive), case recv_status(HSData) of true -> true; false -> ?shutdown(Node) end. do_mark_pending(Kernel,Node,Address,Flags) -> Kernel ! {self(), {accept_pending,Node,Address, publish_type(Flags)}}, receive {Kernel, {accept_pending, Ret}} -> ?trace("do_mark_pending(~p,~p,~p,~p) -> ~p~n", [Kernel,Node,Address,Flags,Ret]), Ret end. is_pending(Kernel, Node) -> Kernel ! {self(), {is_pending, Node}}, receive {Kernel, {is_pending, Reply}} -> Reply end. do_remark_pending(Kernel, Node) -> Kernel ! {self(), {remark_pending, Node}}, receive {Kernel, {remark_pending, ok}} -> ok; {Kernel, {remark_pending, bad_request}} -> %% Can not occur !!??? error_msg("** Simultaneous connect failed : ~p~n", [Node]), ?shutdown(Node) end. %% %% This will tell the net_kernel about the nodedown as it %% recognizes the exit signal. %% Terminate with reason shutdown so inet processes want %% generate crash reports. %% The termination of this process does also imply that the Socket %% is closed in a controlled way by inet_drv. %% shutdown(Line, Data) -> flush_down(), exit(shutdown). %% Use this line to debug connection. %% Set net_kernel verbose = 1 as well. %% exit({shutdown, ?MODULE, Line, Data, erlang:now()}). flush_down() -> receive {From, get_status} -> From ! {self(), get_status, error}, flush_down() after 0 -> ok end. handshake_we_started(#hs_data{other_node = Node, other_version = Version} = HSData) -> send_name(HSData), recv_status(HSData), {Flags, NodeA, VersionA, ChallengeA} = recv_challenge(HSData), if Node =/= NodeA -> ?shutdown(no_node); Version =/= VersionA -> ?shutdown(no_node); true -> true end, NewHSData = HSData#hs_data{other_flags = Flags, other_started = false}, MyChallenge = gen_challenge(), {MyCookie,HisCookie} = get_cookies(Node), send_challenge_reply(NewHSData,MyChallenge, gen_digest(ChallengeA,HisCookie)), reset_timer(NewHSData#hs_data.timer), recv_challenge_ack(NewHSData, MyChallenge, MyCookie), connection(NewHSData). %% -------------------------------------------------------------- %% The connection has been established. %% -------------------------------------------------------------- connection(#hs_data{other_node = Node, socket = Socket, f_address = FAddress, f_setopts_pre_nodeup = FPreNodeup, f_setopts_post_nodeup = FPostNodeup}= HSData) -> cancel_timer(HSData#hs_data.timer), PType = publish_type(HSData#hs_data.other_started, HSData#hs_data.other_flags, HSData#hs_data.this_flags), case do_setnode(HSData) of error -> ?shutdown(Node); ok -> case FPreNodeup(Socket) of ok -> Address = FAddress(Socket,Node), mark_nodeup(HSData,Address), case FPostNodeup(Socket) of ok -> con_loop(HSData#hs_data.kernel_pid, Node, Socket, Address, HSData#hs_data.this_node, PType, #tick{}, HSData#hs_data.mf_tick, HSData#hs_data.mf_getstat); _ -> ?shutdown(Node) end; _ -> ?shutdown(Node) end end. %% Generate a message digest from Challenge number and Cookie gen_digest(Challenge, Cookie) when integer(Challenge), atom(Cookie) -> C0 = erlang:md5_init(), C1 = erlang:md5_update(C0, atom_to_list(Cookie)), C2 = erlang:md5_update(C1, integer_to_list(Challenge)), binary_to_list(erlang:md5_final(C2)). %% --------------------------------------------------------------- %% Challenge code %% gen_challenge() returns a "random" number %% --------------------------------------------------------------- gen_challenge() -> {A,B,C} = erlang:now(), {D,_} = erlang:statistics(reductions), {E,_} = erlang:statistics(runtime), {F,_} = erlang:statistics(wall_clock), {G,H,_} = erlang:statistics(garbage_collection), %% A(8) B(16) C(16) %% D(16),E(8), F(16) G(8) H(16) ( ((A bsl 24) + (E bsl 16) + (G bsl 8) + F) bxor (B + (C bsl 16)) bxor (D + (H bsl 16)) ) band 16#ffffffff. %% %% Get the cookies for a node from auth %% get_cookies(Node) -> case auth:get_cookie(Node) of X when atom(X) -> {X,X}; {Y,Z} when atom(Y), atom(Z) -> {Y,Z}; _ -> erlang:fault("Corrupt cookie database") end. %% %% Setnode works quite differently depending on handshale type: %% With MD5 digests, cookies are already dealt with and the %% distribution code in the emulator need not know about them. %% The cleartext shake on the other hand needs to %% inform the emulator of the cookies, as they are contained in every message. %% do_setnode(#hs_data{other_node = Node, socket = Socket, other_flags = Flags, other_version = Version, this_flags = ThisFlags, other_started = OtherStarted, f_getll = GetLL}) -> case GetLL(Socket) of {ok,Port} -> RFlags = type_flags(OtherStarted, Flags, ThisFlags), ?trace("setnode(md5,~p ~p ~p)~n", [Node, Port, {publish_type(RFlags), '(', RFlags, ')', Version}]), erlang:setnode(Node, Port, {RFlags, Version, '', ''}), ok; _ -> error end. %% type_flags(OtherStarted, Flags, ThisFlags) type_flags(true, Flags, _) -> Flags; type_flags(_, Flags, ThisFlags) -> case ThisFlags band ?DFLAG_PUBLISHED of 0 -> %% hidden Flags bxor ?DFLAG_PUBLISHED; _ -> Flags end. mark_nodeup(#hs_data{kernel_pid = Kernel, other_node = Node, socket = Socket, this_flags = ThisFlags, other_flags = Flags, other_started = OtherStarted}, Address) -> Kernel ! {self(), {nodeup,Node,Address, publish_type(OtherStarted, Flags, ThisFlags), true}}, receive {Kernel, inserted} -> ok; {Kernel, bad_request} -> TypeT = case OtherStarted of true -> "accepting connection"; _ -> "initiating connection" end, error_msg("Fatal: ~p was not allowed to " "send {nodeup, ~p} to kernel when ~s~n", [self(), Node, TypeT]), ?shutdown(Node) end. con_loop(Kernel, Node, Socket, TcpAddress, MyNode, Type, Tick, MFTick, MFGetstat) -> receive {tcp_closed, Socket} -> ?shutdown(Node); {Kernel, disconnect} -> ?shutdown(Node); {Kernel, tick} -> case send_tick(Socket, Tick, Type, MFTick, MFGetstat) of {ok, NewTick} -> con_loop(Kernel, Node, Socket, TcpAddress, MyNode, Type, NewTick, MFTick, MFGetstat); {error, not_responding} -> error_msg("** Node ~p not responding **~n" "** Removing (timedout) connection **~n", [Node]), ?shutdown(Node); Other -> ?shutdown(Node) end; {From, get_status} -> case MFGetstat(Socket) of {ok, Read, Write, _} -> From ! {self(), get_status, {ok, Read, Write}}, con_loop(Kernel, Node, Socket, TcpAddress, MyNode, Type, Tick, MFTick, MFGetstat); _ -> ?shutdown(Node) end end. %% ------------------------------------------------------------ %% Misc. functions. %% ------------------------------------------------------------ send_name(#hs_data{socket = Socket, this_node = Node, f_send = FSend, this_flags = Flags, other_version = Version}) -> ?trace("send_name: node=~w, version=~w\n", [Node,Version]), ?to_port(FSend, Socket, [$n, ?int16(Version), ?int32(Flags), atom_to_list(Node)]). send_challenge(#hs_data{socket = Socket, this_node = Node, other_version = Version, this_flags = Flags, f_send = FSend}, Challenge ) -> ?trace("send: challenge=~w version=~w\n", [Challenge,Version]), ?to_port(FSend, Socket, [$n,?int16(Version), ?int32(Flags), ?int32(Challenge), atom_to_list(Node)]). send_challenge_reply(#hs_data{socket = Socket, f_send = FSend}, Challenge, Digest) -> ?trace("send_reply: challenge=~w digest=~p\n", [Challenge,Digest]), ?to_port(FSend, Socket, [$r,?int32(Challenge),Digest]). send_challenge_ack(#hs_data{socket = Socket, f_send = FSend}, Digest) -> ?trace("send_ack: digest=~p\n", [Digest]), ?to_port(FSend, Socket, [$a,Digest]). %% %% Get the name of the other side. %% Close the connection if invalid data. %% The IP address sent is not interesting (as in the old %% tcp_drv.c which used it to detect simultaneous connection %% attempts). %% recv_name(#hs_data{socket = Socket, f_recv = Recv}) -> case Recv(Socket, 0, infinity) of {ok,Data} -> get_name(Data); _ -> ?shutdown(no_node) end. get_name([$n,VersionA, VersionB, Flag1, Flag2, Flag3, Flag4 | OtherNode]) -> {?u32(Flag1, Flag2, Flag3, Flag4), list_to_atom(OtherNode), ?u16(VersionA,VersionB)}; get_name(Data) -> ?shutdown(Data). %% publish_type(OtherStarted, Flags, ThisFlags) -> %% In case we initiated a hidden connection. publish_type(true, Flags, _) -> publish_type(Flags); publish_type(_, _, ThisFlags) -> publish_type(ThisFlags). publish_type(Flags) -> case Flags band ?DFLAG_PUBLISHED of 0 -> hidden; _ -> normal end. %% wait for challenge after connect recv_challenge(#hs_data{socket = Socket, f_recv = Recv}) -> case Recv(Socket, 0, infinity) of {ok,[$n,V1,V0,Fl1,Fl2,Fl3,Fl4,CA3,CA2,CA1,CA0 | Ns]} -> Flags = ?u32(Fl1,Fl2,Fl3,Fl4), Node =list_to_atom(Ns), Version = ?u16(V1,V0), Challenge = ?u32(CA3,CA2,CA1,CA0), ?trace("recv: node=~w, challenge=~w version=~w\n", [Node, Challenge,Version]), {Flags,Node,Version,Challenge}; _ -> ?shutdown(no_node) end. %% %% wait for challenge response after send_challenge %% recv_challenge_reply(#hs_data{socket = Socket, other_node = NodeB, f_recv = FRecv}, ChallengeA, Cookie) -> case FRecv(Socket, 0, infinity) of {ok,[$r,CB3,CB2,CB1,CB0 | SumB]} when length(SumB) == 16 -> SumA = gen_digest(ChallengeA, Cookie), ChallengeB = ?u32(CB3,CB2,CB1,CB0), ?trace("recv_reply: challenge=~w digest=~p\n", [ChallengeB,SumB]), ?trace("sum = ~p\n", [SumA]), if SumB == SumA -> ChallengeB; true -> error_msg("** Connection attempt from " "disallowed node ~w ** ~n", [NodeB]), ?shutdown(NodeB) end; _ -> ?shutdown(no_node) end. recv_challenge_ack(#hs_data{socket = Socket, f_recv = FRecv, other_node = NodeB}, ChallengeB, CookieA) -> case FRecv(Socket, 0, infinity) of {ok,[$a | SumB]} when length(SumB) == 16 -> SumA = gen_digest(ChallengeB, CookieA), ?trace("recv_ack: digest=~p\n", [SumB]), ?trace("sum = ~p\n", [SumA]), if SumB == SumA -> ok; true -> error_msg("** Connection attempt to " "disallowed node ~w ** ~n", [NodeB]), ?shutdown(NodeB) end; _ -> ?shutdown(NodeB) end. recv_status(#hs_data{kernel_pid = Kernel, socket = Socket, other_node = Node, f_recv = Recv} = HSData) -> case Recv(Socket, 0, infinity) of {ok, [$s|StrStat]} -> Stat = list_to_atom(StrStat), ?debug({dist_util,self(),recv_status, Node, Stat}), case Stat of not_allowed -> ?shutdown(Node); nok -> %% wait to be killed by net_kernel receive after infinity -> ok end; alive -> Reply = is_pending(Kernel, Node), ?debug({is_pending,self(),Reply}), send_status(HSData, Reply), if Reply == false -> ?shutdown(Node); Reply == true -> Stat end; _ -> Stat end; Error -> ?debug({dist_util,self(),recv_status_error, Node, Error}), ?shutdown(Node) end. send_status(#hs_data{socket = Socket, other_node = Node, f_send = FSend}, Stat) -> ?debug({dist_util,self(),send_status, Node, Stat}), case FSend(Socket, [$s | atom_to_list(Stat)]) of {error, _} -> ?shutdown(Node); _ -> true end. %% %% Send a TICK to the other side. %% %% This will happen every 15 seconds (by default) %% The idea here is that every 15 secs, we write a little %% something on the connection if we haven't written anything for %% the last 15 secs. %% This will ensure that nodes that are not responding due to %% hardware errors (Or being suspended by means of ^Z) will %% be considered to be down. If we do not want to have this %% we must start the net_kernel (in erlang) without its %% ticker process, In that case this code will never run %% And then every 60 seconds we also check the connection and %% close it if we havn't received anything on it for the %% last 60 secs. If ticked == tick we havn't received anything %% on the connection the last 60 secs. %% The detection time interval is thus, by default, 45s < DT < 75s %% A HIDDEN node is always (if not a pending write) ticked if %% we haven't read anything as a hidden node only ticks when it receives %% a TICK !! send_tick(Socket, Tick, Type, MFTick, MFGetstat) -> #tick{tick = T0, read = Read, write = Write, ticked = Ticked} = Tick, T = T0 + 1, T1 = T rem 4, case MFGetstat(Socket) of {ok, Read, _, _} when Ticked == T -> {error, not_responding}; {ok, Read, W, Pend} when Type == hidden -> send_tick(Socket, Pend, MFTick), {ok, Tick#tick{write = W + 1, tick = T1}}; {ok, Read, Write, Pend} -> send_tick(Socket, Pend, MFTick), {ok, Tick#tick{write = Write + 1, tick = T1}}; {ok, R, Write, Pend} -> send_tick(Socket, Pend, MFTick), {ok, Tick#tick{write = Write + 1, read = R, tick = T1, ticked = T}}; {ok, Read, W, _} -> {ok, Tick#tick{write = W, tick = T1}}; {ok, R, W, _} -> {ok, Tick#tick{write = W, read = R, tick = T1, ticked = T}}; Error -> Error end. send_tick(Socket, 0, MFTick) -> MFTick(Socket); send_tick(_, Pend, _) -> %% Dont send tick if pending write. ok. %% ------------------------------------------------------------ %% Connection setup timeout timer. %% After Timeout milliseconds this process terminates %% which implies that the owning setup/accept process terminates. %% The timer is reset before every network operation during the %% connection setup ! %% ------------------------------------------------------------ start_timer(Timeout) -> spawn_link(?MODULE, setup_timer, [self(), Timeout*?trace_factor]). setup_timer(Pid, Timeout) -> receive {Pid, reset} -> setup_timer(Pid, Timeout) after Timeout -> ?trace("Timer expires ~p, ~p~n",[Pid, Timeout]), ?shutdown(timer) end. reset_timer(Timer) -> Timer ! {self(), reset}. cancel_timer(Timer) -> unlink(Timer), exit(Timer, shutdown). -------------- next part -------------- %% ``The contents of this file are subject to the Erlang Public License, %% Version 1.1, (the "License"); you may not use this file except in %% compliance with the License. You should have received a copy of the %% Erlang Public License along with this software. If not, it can be %% retrieved via the world wide web at http://www.erlang.org/. %% %% Software distributed under the License is distributed on an "AS IS" %% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See %% the License for the specific language governing rights and limitations %% under the License. %% %% The Initial Developer of the Original Code is Ericsson Utvecklings AB. %% Portions created by Ericsson are Copyright 1999, Ericsson Utvecklings %% AB. All Rights Reserved.'' %% %% $Id: net_kernel.erl,v 1.8 2000/12/13 07:44:17 magnus Exp $ %% -module(net_kernel). -behaviour(gen_server). -define(nodedown(N, State), verbose({?MODULE, ?LINE, nodedown, N}, 1, State)). -define(nodeup(N, State), verbose({?MODULE, ?LINE, nodeup, N}, 1, State)). %%-define(dist_debug, true). %-define(DBG,erlang:display([?MODULE,?LINE])). -ifdef(dist_debug). -define(debug(Term), erlang:display(Term)). -else. -define(debug(Term), ok). -endif. %% User Interface Exports -export([start/1, start_link/1, stop/0, kernel_apply/3, monitor_nodes/1, longnames/0, allow/1, protocol_childspecs/0, epmd_module/0]). -export([connect/1, disconnect/1, hidden_connect/1]). -export([connect_node/1, hidden_connect_node/1]). %% explicit connect -export([node_info/1, node_info/2, nodes_info/0, connecttime/0, i/0, i/1, verbose/1]). %% Internal Exports -export([do_spawn_link/5, ticker/2, do_nodeup/2]). -export([init/1,handle_call/3,handle_cast/2,handle_info/2, terminate/2]). -import(error_logger,[error_msg/2]). -record(state, { name, %% The node name node, %% The node name including hostname type, %% long or short names ticktime, %% tick other nodes regularly connecttime, %% the connection setuptime. connections, %% table of connections conn_owners = [], %% List of connection owner pids, pend_owners = [], %% List of potential owners conn_pid = [], %% All pending and up connection pids %% used for cleanup of really crashed %% (e.g. exit(Owner, kill)) connections !! listen, %% list of #listen monitor, %% list of monitors for nodeup/nodedown pending_nodeup = [], allowed, %% list of allowed nodes in a restricted system verbose = 0 %def_verb() %% level of verboseness }). -record(listen, { listen, %% listen pid accept, %% accepting pid address, %% #net_address module %% proto module }). -define(LISTEN_ID, #listen.listen). -define(ACCEPT_ID, #listen.accept). -record(pend_nodeup, {node, pid}). -record(connection, { node, %% remote node name state, %% pending | up | up_pending owner, %% owner pid pending_owner, %% possible new owner address, %% #net_address waiting = [], %% queued processes type %% normal | hidden }). -record(barred_connection, { node %% remote node name }). %% Default connection setup timeout in milliseconds. %% This timeout is set for every distributed action during %% the connection setup. -define(SETUPTIME, 7000). -include("net_address.hrl"). %% Interface functions kernel_apply(M,F,A) -> request({apply,M,F,A}). allow(Nodes) -> request({allow, Nodes}). monitor_nodes(Flag) -> request({monitor_nodes, Flag}). longnames() -> request(longnames). stop() -> erl_distribution:stop(). node_info(Node) -> get_node_info(Node). node_info(Node, Key) -> get_node_info(Node, Key). nodes_info() -> get_nodes_info(). i() -> print_info(). i(Node) -> print_info(Node). verbose(Level) when integer(Level) -> request({verbose, Level}). %% Called though BIF's connect(Node) -> case application:get_env(kernel, hidden) of {ok, true} -> hidden_connect(Node); _ -> connect(Node, normal) end. disconnect(Node) -> request({disconnect, Node}). %% connect but not seen hidden_connect(Node) -> connect(Node, hidden). %% explicit connects connect_node(Node) when atom(Node) -> request({connect, normal, Node}). hidden_connect_node(Node) when atom(Node) -> request({connect, hidden, Node}). connect(Node, Type) -> %% Type = normal | hidden case ets:lookup(sys_dist, Node) of [#barred_connection{}] -> false; _ -> case application:get_env(kernel, dist_auto_connect) of {ok, never} -> false; _ -> request({connect, Type, Node}) end end. %% If the net_kernel isn't running we ignore all requests to the %% kernel, thus basically accepting them :-) request(Req) -> case whereis(net_kernel) of P when pid(P) -> gen_server:call(net_kernel,Req,infinity); Other -> ignored end. %% This function is used to dynamically start the %% distribution. start(Args) -> erl_distribution:start(Args). %% This is the main startup routine for net_kernel %% The defaults are longnames and a ticktime of 15 secs to the tcp_drv. start_link([Name]) -> start_link([Name, longnames]); start_link([Name, LongOrShortNames]) -> start_link([Name, LongOrShortNames, 15000]); start_link([Name, LongOrShortNames, Ticktime]) -> case gen_server:start_link({local, net_kernel}, net_kernel, {Name, LongOrShortNames, Ticktime}, []) of {ok, Pid} -> {ok, Pid}; {error, {already_started, Pid}} -> {ok, Pid}; Error -> exit(nodistribution) end. init({Name, LongOrShortNames, Ticktime}) -> process_flag(trap_exit,true), case init_node(Name, LongOrShortNames) of {ok, Node, Listeners} -> process_flag(priority, max), spawn_link(net_kernel, ticker, [self(), Ticktime]), case auth:get_cookie(Node) of Cookie when atom(Cookie) -> Monitor = std_monitors(), send_list(Monitor, {nodeup, Node}), {ok, #state{name = Name, node = Node, type = LongOrShortNames, ticktime = Ticktime, connecttime = connecttime(), connections = ets:new(sys_dist,[named_table, protected, {keypos, 2}]), listen = Listeners, monitor = Monitor, allowed = [], verbose = 0 }}; _ELSE -> {stop, {error,{bad_cookie, Node}}} end; Error -> {stop, Error} end. %% ------------------------------------------------------------ %% handle_call. %% ------------------------------------------------------------ %% %% Set up a connection to Node. %% The response is delayed until the connection is up and %% running. %% handle_call({connect, _, Node}, From, State) when Node == node() -> {reply, true, State}; handle_call({connect, Type, Node}, From, State) -> verbose({connect, Type, Node}, 1, State), case ets:lookup(sys_dist, Node) of [Conn] when Conn#connection.state == up -> {reply, true, State}; [Conn] when Conn#connection.state == pending -> Waiting = Conn#connection.waiting, ets:insert(sys_dist, Conn#connection{waiting = [From|Waiting]}), {noreply, State}; [Conn] when Conn#connection.state == up_pending -> Waiting = Conn#connection.waiting, ets:insert(sys_dist, Conn#connection{waiting = [From|Waiting]}), {noreply, State}; _ -> case setup(Node,Type,From,State) of {ok, SetupPid} -> Owners = [{SetupPid, Node} | State#state.conn_owners], Conn = [SetupPid | State#state.conn_pid], {noreply, State#state{conn_owners = Owners, conn_pid = Conn}}; _ -> {reply, false, State} end end; %% %% Close the connection to Node. %% handle_call({disconnect, Node}, From, State) when Node == node() -> {reply, false, State}; handle_call({disconnect, Node}, From, State) -> verbose({disconnect, Node}, 1, State), {Reply, State1} = do_disconnect(Node, State), {reply, Reply, State1}; %% %% The spawn/4 BIF ends up here. %% handle_call({spawn,M,F,A,Gleader}, {From,Tag}, State) when pid(From) -> Pid = (catch spawn(M,F,A)), group_leader(Gleader,Pid), {reply,Pid,State}; %% %% The spawn_link/4 BIF ends up here. %% handle_call({spawn_link,M,F,A,Gleader}, {From,Tag}, State) when pid(From) -> catch spawn(net_kernel,do_spawn_link,[{From,Tag},M,F,A,Gleader]), {noreply,State}; %% %% Only allow certain nodes. %% handle_call({allow, Nodes}, _From, State) -> case all_atoms(Nodes) of true -> Allowed = State#state.allowed, {reply,ok,State#state{allowed = Allowed ++ Nodes}}; false -> {reply,error,State} end; %% %% Toggle monitor of all nodes. Pid receives {nodeup, Node} %% and {nodedown, Node} whenever a node appears/disappears. %% handle_call({monitor_nodes, Flag}, {Pid, _}, State0) -> {Res, State} = monitor_nodes(Flag, Pid, State0), {reply,Res,State}; %% %% authentication, used by auth. Simply works as this: %% if the message comes through, the other node IS authorized. %% handle_call({is_auth, Node}, _From, State) -> {reply,yes,State}; %% %% Not applicable any longer !? %% handle_call({apply,Mod,Fun,Args}, {From,Tag}, State) when pid(From), node(From) == node() -> gen_server:reply({From,Tag}, not_implemented), % Port = State#state.port, % catch apply(Mod,Fun,[Port|Args]), {noreply,State}; handle_call(longnames, _From, State) -> {reply, get(longnames), State}; handle_call({verbose, Level}, _From, State) -> {reply, State#state.verbose, State#state{verbose = Level}}. %% ------------------------------------------------------------ %% handle_cast. %% ------------------------------------------------------------ handle_cast(_, State) -> {noreply,State}. %% ------------------------------------------------------------ %% terminate. %% ------------------------------------------------------------ terminate(no_network, State) -> lists:foreach( fun(Node) -> ?nodedown(Node, State), send_list(State#state.monitor, {nodedown,Node}) end, get_nodes(up) ++ [node()]); terminate(_Reason, State) -> lists:foreach( fun(#listen {listen = Listen,module = Mod}) -> Mod:close(Listen) end, State#state.listen), lists:foreach( fun(Node) -> ?nodedown(Node, State), send_list(State#state.monitor, {nodedown,Node}) end, get_nodes(up) ++ [node()]). %% ------------------------------------------------------------ %% handle_info. %% ------------------------------------------------------------ %% %% accept a new connection. %% handle_info({accept,AcceptPid,Socket,Family,Proto}, State) -> MyNode = State#state.node, case get_proto_mod(Family,Proto,State#state.listen) of {ok, Mod} -> Pid = Mod:accept_connection(AcceptPid, Socket, MyNode, State#state.allowed, State#state.connecttime), AcceptPid ! {self(), controller, Pid}, {noreply, State#state { conn_pid = [Pid | State#state.conn_pid] }}; _ -> AcceptPid ! {self(), unsupported_protocol}, {noreply, State} end; %% %% A node has successfully been connected. %% handle_info({SetupPid, {nodeup,Node,Address,Type,Immediate}}, State) -> verbose({nodeup, Node, Type}, 1, State), case ets:lookup(sys_dist, Node) of [Conn] when Conn#connection.state == pending, Conn#connection.owner == SetupPid -> ets:insert(sys_dist, Conn#connection{state = up, address = Address, waiting = [], type = Type}), SetupPid ! {self(), inserted}, reply_waiting(Conn#connection.waiting, true), case Type of normal -> case Immediate of true -> send_list(State#state.monitor, {nodeup, Node}), {noreply, State}; _ -> Pid = spawn_link(net_kernel, do_nodeup, [self(), Node]), Pending = State#state.pending_nodeup, {noreply, State#state{pending_nodeup = [#pend_nodeup{node = Node, pid = Pid} | Pending]}} end; hidden -> {noreply, State} end; _ -> SetupPid ! {self(), bad_request}, {noreply, State} end; handle_info({From,nodeup,Node}, State) -> Pending = State#state.pending_nodeup, case lookup_pend(Node, Pending) of {ok, NodeUp} when NodeUp#pend_nodeup.pid == From -> ?nodeup(Node, State), send_list(State#state.monitor, {nodeup, Node}), {noreply, State#state{pending_nodeup = del_pend(Node, Pending)}}; _ -> {noreply,State} end; %% %% Mark a node as pending (accept) if not busy. %% handle_info({AcceptPid, {accept_pending,Node,Address,Type}}, State) -> case ets:lookup(sys_dist, Node) of [Conn] when Conn#connection.state == pending -> AcceptPid ! {self(), {accept_pending, pending}}, {noreply, State}; [Conn] when Conn#connection.state == up -> AcceptPid ! {self(), {accept_pending, up_pending}}, ets:insert(sys_dist, Conn#connection { pending_owner = AcceptPid, state = up_pending }), Pend = [{AcceptPid, Node} | State#state.pend_owners ], {noreply, State#state { pend_owners = Pend }}; [Conn] when Conn#connection.state == up_pending -> AcceptPid ! {self(), {accept_pending, already_pending}}, {noreply, State}; _ -> ets:insert(sys_dist, #connection{node = Node, state = pending, owner = AcceptPid, address = Address, type = Type}), AcceptPid ! {self(), {accept_pending, ok}}, Owners = [{AcceptPid, Node} | State#state.conn_owners], {noreply, State#state{conn_owners = Owners}} end; %% %% A simultaneous connect has been detected and we want to %% change pending process. %% handle_info({AcceptPid, {remark_pending, Node}}, State) -> case ets:lookup(sys_dist, Node) of [Conn] when Conn#connection.state == pending -> OldOwner = Conn#connection.owner, ?debug({net_kernel, remark, old, OldOwner, new, AcceptPid}), exit(OldOwner, remarked), receive {'EXIT', OldOwner, _} -> true end, Owners = lists:keyreplace(OldOwner, 1, State#state.conn_owners, {AcceptPid, Node}), ets:insert(sys_dist, Conn#connection{owner = AcceptPid}), AcceptPid ! {self(), {remark_pending, ok}}, State1 = remove_conn_pid(OldOwner, State#state{conn_owners = Owners}), {noreply, State1}; _ -> AcceptPid ! {self(), {remark_pending, bad_request}}, {noreply, State} end; handle_info({SetupPid, {is_pending, Node}}, State) -> Reply = lists:member({SetupPid,Node},State#state.conn_owners), SetupPid ! {self(), {is_pending, Reply}}, {noreply, State}; %% %% Handle different types of process terminations. %% handle_info({'EXIT', From, Reason}, State) when pid(From) -> verbose({'EXIT', From, Reason}, 1, State), handle_exit(From, State); %% %% Handle badcookie and badname messages ! %% handle_info({From,registered_send,To,Mess},State) -> send(From,To,Mess), {noreply,State}; %% badcookies SHOULD not be sent %% (if someone does erlang:set_cookie(node(),foo) this may be) handle_info({From, badcookie, To ,Mess}, State) -> error_logger:error_msg("~n** Got OLD cookie from ~w~n", [getnode(From)]), {_Reply, State1} = do_disconnect(getnode(From), State), {noreply,State1}; %% %% Tick all connections. %% handle_info(tick, State) -> lists:foreach(fun({Pid,_Node}) -> Pid ! {self(), tick} end, State#state.conn_owners), {noreply,State}; handle_info({From, {set_monitors, L}}, State) -> From ! {net_kernel, done}, {noreply,State#state{monitor = L}}; handle_info(X, State) -> error_msg("Net kernel got ~w~n",[X]), {noreply,State}. %% ----------------------------------------------------------- %% Handle exit signals. %% We have 5 types of processes to handle. %% %% 1. The Listen process. %% 2. The Accept process. %% 3. Connection owning processes. %% 4. Pending check nodeup processes. %% 5. Processes monitoring nodeup/nodedown. %% (6. Garbage pid.) %% %% The process type function that handled the process throws %% the handle_info return value ! %% ----------------------------------------------------------- handle_exit(Pid, State) -> catch do_handle_exit(Pid, State). do_handle_exit(Pid, State) -> State1 = remove_conn_pid(Pid, State), listen_exit(Pid, State1), accept_exit(Pid, State1), conn_own_exit(Pid, State1), nodeup_exit(Pid, State1), monitor_exit(Pid, State1), pending_own_exit(Pid, State1), {noreply, State1}. remove_conn_pid(Pid, State) -> State#state { conn_pid = State#state.conn_pid -- [Pid] }. listen_exit(Pid, State) -> case lists:keysearch(Pid, ?LISTEN_ID, State#state.listen) of {value, _} -> error_msg("** Netkernel terminating ... **\n", []), throw({stop,no_network,State}); _ -> false end. accept_exit(Pid, State) -> Listen = State#state.listen, case lists:keysearch(Pid, ?ACCEPT_ID, Listen) of {value, ListenR} -> ListenS = ListenR#listen.listen, Mod = ListenR#listen.module, AcceptPid = Mod:accept(ListenS), L = lists:keyreplace(Pid, ?ACCEPT_ID, Listen, ListenR#listen{accept = AcceptPid}), throw({noreply, State#state{listen = L}}); _ -> false end. conn_own_exit(Pid, State) -> Owners = State#state.conn_owners, case lists:keysearch(Pid, 1, Owners) of {value, {Pid, Node}} -> throw({noreply, nodedown(Pid, Node, State)}); _ -> false end. nodeup_exit(Pid, State) -> Pending = State#state.pending_nodeup, case del_pend(Pid, Pending) of Pending -> false; NewPend -> throw({noreply, State#state{pending_nodeup = NewPend}}) end. monitor_exit(Pid, State) -> Monitor = State#state.monitor, case delete_all(Pid, Monitor) of Monitor -> false; NewMonitor -> throw({noreply, State#state{monitor = NewMonitor}}) end. pending_own_exit(Pid, State) -> Pend = State#state.pend_owners, case lists:keysearch(Pid, 1, Pend) of {value, {Pid, Node}} -> NewPend = lists:keydelete(Pid, 1, Pend), State1 = State#state { pend_owners = NewPend }, case get_conn(Node) of {ok, Conn} when Conn#connection.state == up_pending -> reply_waiting(Conn#connection.waiting, true), Conn1 = Conn#connection { state = up, waiting = [], pending_owner = undefined }, ets:insert(sys_dist, Conn1); _ -> ok end, throw({noreply, State1}); _ -> false end. %% ----------------------------------------------------------- %% A node has gone down !! %% nodedown(Owner, Node, State) -> State' %% ----------------------------------------------------------- nodedown(Owner, Node, State) -> case get_conn(Node) of {ok, Conn} -> nodedown(Conn, Owner, Node, Conn#connection.type, State); _ -> State end. get_conn(Node) -> case ets:lookup(sys_dist, Node) of [Conn = #connection{}] -> {ok, Conn}; _ -> error end. nodedown(Conn, Owner, Node, Type, OldState) -> Owners = lists:keydelete(Owner, 1, OldState#state.conn_owners), State = OldState#state{conn_owners = Owners}, case Conn#connection.state of pending when Conn#connection.owner == Owner -> pending_nodedown(Conn, Node, Type, State); up when Conn#connection.owner == Owner -> up_nodedown(Conn, Node, Type, State); up_pending when Conn#connection.owner == Owner -> up_pending_nodedown(Conn, Node, Type, State); _ -> OldState end. pending_nodedown(Conn, Node, Type, State) -> mark_sys_dist_nodedown(Node), reply_waiting(Conn#connection.waiting, false), case Type of normal -> ?nodedown(Node, State), send_list(State#state.monitor, {nodedown, Node}); _ -> ok end, State. up_pending_nodedown(Conn, Node, Type, State) -> AcceptPid = Conn#connection.pending_owner, Owners = State#state.conn_owners, Pend = lists:keydelete(AcceptPid, 1, State#state.pend_owners), case Type of normal -> send_list(State#state.monitor, {nodedown, Node}); _ -> ok end, Conn1 = Conn#connection { owner = AcceptPid, pending_owner = undefined, state = pending }, ets:insert(sys_dist, Conn1), AcceptPid ! {self(), pending}, State#state{conn_owners = [{AcceptPid,Node}|Owners], pend_owners = Pend}. up_nodedown(Conn, Node, Type, State) -> mark_sys_dist_nodedown(Node), case Type of normal -> ?nodedown(Node, State), send_list(State#state.monitor, {nodedown, Node}), Pending = State#state.pending_nodeup, case lookup_pend(Node, Pending) of {ok, NodeUp} -> Pid = NodeUp#pend_nodeup.pid, unlink(Pid), exit(Pid, kill), State#state{pending_nodeup = del_pend(Pid, Pending)}; _ -> State end; _ -> State end. mark_sys_dist_nodedown(Node) -> case application:get_env(kernel, dist_auto_connect) of {ok, once} -> ets:insert(sys_dist, #barred_connection{node = Node}); _ -> ets:delete(sys_dist, Node) end. %% ----------------------------------------------------------- %% End handle_exit/2 !! %% ----------------------------------------------------------- %% A process wants to toggle monitoring nodeup/nodedown from nodes. monitor_nodes(true, Pid, State) -> %% Used to monitor all changes in the nodes list link(Pid), Monitor = State#state.monitor, {ok, State#state{monitor = [Pid|Monitor]}}; monitor_nodes(false, Pid, State) -> Monitor = State#state.monitor, State1 = State#state{monitor = delete_all(Pid,Monitor)}, do_unlink(Pid, State1), {ok, State1}; monitor_nodes(_, _, State) -> {error, State}. %% do unlink if we have no more references to Pid. do_unlink(Pid, State) -> case lists:member(Pid, State#state.monitor) of true -> false; _ -> unlink(Pid) end. do_disconnect(Node, State) -> case ets:lookup(sys_dist, Node) of [Conn] when Conn#connection.state == up -> disconnect_pid(Conn#connection.owner, State); [Conn] when Conn#connection.state == up_pending -> disconnect_pid(Conn#connection.owner, State); _ -> {false, State} end. disconnect_pid(Pid, State) -> exit(Pid, disconnect), %% Sync wait for connection to die!!! receive {'EXIT', Pid, Reason} -> {_,State1} = handle_exit(Pid, State), {true, State1} end. %% %% %% get_nodes(Which) -> get_nodes(ets:first(sys_dist), Which). get_nodes('$end_of_table', _) -> []; get_nodes(Key, Which) -> case ets:lookup(sys_dist, Key) of [Conn = #connection{state = up}] -> [Conn#connection.node | get_nodes(ets:next(sys_dist, Key), Which)]; [Conn = #connection{}] when Which == all -> [Conn#connection.node | get_nodes(ets:next(sys_dist, Key), Which)]; _ -> get_nodes(ets:next(sys_dist, Key), Which) end. -ifdef(NOTUSED). stop_dist([], _) -> ok; stop_dist([Node|Nodes], Monitor) -> send_list(Monitor, {nodedown, Node}), stop_dist(Nodes, Monitor). -endif. ticker(Kernel, Tick) -> process_flag(priority, max), ticker1(Kernel, to_integer(Tick)). to_integer(T) when integer(T) -> T; to_integer(T) when atom(T) -> list_to_integer(atom_to_list(T)). ticker1(Kernel, Tick) -> receive after Tick -> Kernel ! tick, ticker1(Kernel, Tick) end. send(From,To,Mess) -> case whereis(To) of undefined -> Mess; P when pid(P) -> P ! Mess end. safesend(Name,Mess) when atom(Name) -> case whereis(Name) of undefined -> Mess; P when pid(P) -> P ! Mess end; safesend(Pid, Mess) -> Pid ! Mess. send_list([P|T], M) -> safesend(P, M), send_list(T, M); send_list([], _) -> ok. %% This code is really intricate. The link will go first and then comes %% the pid, This means that the client need not do a network link. %% If the link message would not arrive, the runtime system shall %% generate a nodedown message do_spawn_link({From,Tag},M,F,A,Gleader) -> link(From), gen_server:reply({From,Tag},self()), %% ahhh group_leader(Gleader,self()), apply(M,F,A). %% ----------------------------------------------------------- %% Set up connection to a new node. %% ----------------------------------------------------------- setup(Node,Type,From,State) -> Allowed = State#state.allowed, case lists:member(Node, Allowed) of false when Allowed /= [] -> error_msg("** Connection attempt with " "disallowed node ~w ** ~n", [Node]), {error, bad_node}; _ -> case select_mod(Node, State#state.listen) of {ok, L} -> Mod = L#listen.module, LAddr = L#listen.address, MyNode = State#state.node, Pid = Mod:setup(Node, Type, MyNode, State#state.type, State#state.connecttime), Addr = LAddr#net_address { address = undefined, host = undefined }, ets:insert(sys_dist, #connection{node = Node, state = pending, owner = Pid, waiting = [From], address = Addr, type = Type}), {ok, Pid}; Error -> Error end end. %% %% Find a module that is willing to handle connection setup to Node %% select_mod(Node, [L|Ls]) -> Mod = L#listen.module, case Mod:select(Node) of true -> {ok, L}; false -> select_mod(Node, Ls) end; select_mod(Node, []) -> {error, {unsupported_address_type, Node}}. get_proto_mod(Family,Protocol,[L|Ls]) -> A = L#listen.address, if A#net_address.family == Family, A#net_address.protocol == Protocol -> {ok, L#listen.module}; true -> get_proto_mod(Family,Protocol,Ls) end; get_proto_mod(Family,Protocol,[]) -> error. %% ----------------------------------------------------------- %% Check if we are authorized after a second. %% ----------------------------------------------------------- do_nodeup(Kernel, Node) -> receive after 1000 -> ok %% sleep a sec, end, case lists:member(Node, nodes()) of false -> exit(normal); true -> ok end, % We will certainly be authenticated if the node is up. % case auth:is_auth(Node) of % yes -> Kernel ! {self(), nodeup, Node}; % Other -> exit(normal) % end. Kernel ! {self(), nodeup, Node}. lookup_pend(Node, [NodeUp|_]) when NodeUp#pend_nodeup.node == Node -> {ok, NodeUp}; lookup_pend(Node, [_|Pending]) -> lookup_pend(Node, Pending); lookup_pend(Node, []) -> false. del_pend(Node, [NodeUp|T]) when NodeUp#pend_nodeup.node == Node -> T; del_pend(Pid, [NodeUp|T]) when NodeUp#pend_nodeup.pid == Pid -> T; del_pend(Key, [NodeUp|T]) -> [NodeUp|del_pend(Key, T)]; del_pend(_, []) -> []. %% -------- Initialisation functions ------------------------ %% never called could be removed! %% was intended to be used to set default value for verbos in the %% state record %%def_verb() -> %% case init:get_argument(net_kernel_verbose) of %% {ok, [[Level]]} -> %% case catch list_to_integer(Level) of %% Int when integer(Int) -> Int; %% _ -> 0 %% end; %% _ -> %% 0 %% end. init_node(Name, LongOrShortNames) -> {NameWithoutHost,Host} = lists:splitwith(fun($@)->false;(_)->true end, atom_to_list(Name)), case create_name(Name, LongOrShortNames) of {ok,Node} -> case start_protos(list_to_atom(NameWithoutHost),Node) of {ok, Ls} -> {ok, Node, Ls}; Error -> Error end; Error -> Error end. %% Create the node name create_name(Name, LongOrShortNames) -> put(longnames, case LongOrShortNames of shortnames -> false; longnames -> true end), {Head,Host1} = create_hostpart(Name,LongOrShortNames), case Host1 of {ok, HostPart} -> {ok,list_to_atom(Head ++ HostPart)}; {error,Type} -> error_logger:info_msg( lists:concat(["Can\'t set ", Type, " node name!\n" "Please check your configuration\n"])), {error,badarg} end; create_name(Name, _) -> {error, badarg}. create_hostpart(Name,LongOrShortNames) -> {Head,Host} = lists:splitwith(fun($@)->false;(_)->true end, atom_to_list(Name)), Host1 = case {Host,LongOrShortNames} of {[$@,_|_],longnames} -> {ok,Host}; {[$@,_|_],shortnames} -> case lists:member($.,Host) of true -> {error,short}; _ -> {ok,Host} end; {_,shortnames} -> case inet_db:gethostname() of H when list(H), length(H)>0 -> {ok,"@" ++ H}; _ -> {error,short} end; {_,longnames} -> case {inet_db:gethostname(),inet_db:res_option(domain)} of {H,D} when list(D),list(H),length(D)> 0, length(H)>0 -> {ok,"@" ++ H ++ "." ++ D}; _ -> {error,long} end end, {Head,Host1}. %% %% %% protocol_childspecs() -> case init:get_argument(proto_dist) of {ok, [Protos]} -> protocol_childspecs(Protos); _ -> protocol_childspecs(["inet_tcp"]) end. protocol_childspecs([]) -> []; protocol_childspecs([H|T]) -> Mod = list_to_atom(H ++ "_dist"), case (catch Mod:childspecs()) of {ok, Childspecs} when list(Childspecs) -> Childspecs ++ protocol_childspecs(T); _ -> protocol_childspecs(T) end. %% %% epmd_module() -> module_name of erl_epmd or similar gen_server_module. %% epmd_module() -> case init:get_argument(epmd_module) of {ok,[[Module]]} -> Module; _ -> erl_epmd end. %% %% Start all protocols %% start_protos(Name,Node) -> case init:get_argument(proto_dist) of {ok, [Protos]} -> start_protos(Name,Protos, Node); _ -> start_protos(Name,["inet_tcp"], Node) end. start_protos(Name,Ps, Node) -> case start_protos(Name, Ps, Node, []) of [] -> {error, badarg}; Ls -> {ok, Ls} end. start_protos(Name, [Proto | Ps], Node, Ls) -> Mod = list_to_atom(Proto ++ "_dist"), case Mod:listen(Name) of {ok, {Socket, Address, Creation}} -> AcceptPid = Mod:accept(Socket), (catch erlang:setnode(Node, Creation)), %% May fail. auth:sync_cookie(), L = #listen { listen = Socket, address = Address, accept = AcceptPid, module = Mod }, start_protos(Name,Ps, Node, [L|Ls]); {'EXIT', {undef,_}} -> error_logger:info_msg("Protocol: ~p: not supported~n", [Proto]), start_protos(Name,Ps, Node, Ls); {'EXIT', Reason} -> error_logger:info_msg("Protocol: ~p: register error: ~p~n", [Proto, Reason]), start_protos(Name,Ps, Node, Ls); {error, duplicate_name} -> error_logger:info_msg("Protocol: ~p: the name " ++ atom_to_list(Node) ++ " seems to be in use by another Erlang node", [Proto]), start_protos(Name,Ps, Node, Ls); {error, Reason} -> error_logger:info_msg("Protocol: ~p: register/listen error: ~p~n", [Proto, Reason]), start_protos(Name,Ps, Node, Ls) end; start_protos(_,[], Node, Ls) -> Ls. %std_monitors() -> [global_name_server]. std_monitors() -> [global_group]. connecttime() -> case application:get_env(kernel, net_setuptime) of {ok, Time} when integer(Time), Time > 0, Time < 120 -> Time * 1000; _ -> ?SETUPTIME end. %% -------- End initialisation functions -------------------- %% ------------------------------------------------------------ %% Node informaion. %% ------------------------------------------------------------ get_node_info(Node) -> case ets:lookup(sys_dist, Node) of [Conn = #connection{owner = Owner, state = State}] -> case get_status(Owner, Node, State) of {ok, In, Out} -> {ok, [{owner, Owner}, {state, State}, {address, Conn#connection.address}, {type, Conn#connection.type}, {in, In}, {out, Out}]}; _ -> {error, bad_node} end; _ -> {error, bad_node} end. %% %% We can't do monitor_node here incase the node is pending, %% the monitor_node/2 call hangs until the connection is ready. %% We will not ask about in/out information either for pending %% connections as this also would block this call awhile. %% get_status(Owner, Node, up) -> monitor_node(Node, true), Owner ! {self(), get_status}, receive {Owner, get_status, Res} -> monitor_node(Node, false), Res; {nodedown, Node} -> error end; get_status(_, _, _) -> {ok, 0, 0}. get_node_info(Node, Key) -> case get_node_info(Node) of {ok, Info} -> case lists:keysearch(Key, 1, Info) of {value, {Key, Value}} -> {ok, Value}; _ -> {error, invalid_key} end; Error -> Error end. get_nodes_info() -> get_nodes_info(get_nodes(all), []). get_nodes_info([Node|Nodes], InfoList) -> case get_node_info(Node) of {ok, Info} -> get_nodes_info(Nodes, [{Node, Info}|InfoList]); _ -> get_nodes_info(Nodes, InfoList) end; get_nodes_info([], InfoList) -> {ok, InfoList}. %% ------------------------------------------------------------ %% Misc. functions %% ------------------------------------------------------------ reply_waiting(Waiting, Rep) -> reply_waiting1(lists:reverse(Waiting), Rep). reply_waiting1([From|W], Rep) -> gen_server:reply(From, Rep), reply_waiting1(W, Rep); reply_waiting1([], _) -> ok. delete_all(From, [From |Tail]) -> delete_all(From, Tail); delete_all(From, [H|Tail]) -> [H|delete_all(From, Tail)]; delete_all(_, []) -> []. all_atoms([]) -> true; all_atoms([N|Tail]) when atom(N) -> all_atoms(Tail); all_atoms(_) -> false. %% ------------------------------------------------------------ %% Print status information. %% ------------------------------------------------------------ print_info() -> nformat("Node", "State", "Type", "In", "Out", "Address"), {ok, NodesInfo} = nodes_info(), {In,Out} = lists:foldl(fun display_info/2, {0,0}, NodesInfo), nformat("Total", "", "", integer_to_list(In), integer_to_list(Out), ""). display_info({Node, Info}, {I,O}) -> State = atom_to_list(fetch(state, Info)), In = fetch(in, Info), Out = fetch(out, Info), Type = atom_to_list(fetch(type, Info)), Address = fmt_address(fetch(address, Info)), nformat(atom_to_list(Node), State, Type, integer_to_list(In), integer_to_list(Out), Address), {I+In,O+Out}. fmt_address(undefined) -> "-"; fmt_address(A) -> case A#net_address.family of inet -> case A#net_address.address of {IP,Port} -> inet_parse:ntoa(IP) ++ ":" ++ integer_to_list(Port); _ -> "-" end; inet6 -> case A#net_address.address of {IP,Port} -> inet_parse:ntoa(IP) ++ "/" ++ integer_to_list(Port); _ -> "-" end; _ -> lists:flatten(io_lib:format("~p", [A#net_address.address])) end. fetch(Key, Info) -> case lists:keysearch(Key, 1, Info) of {value, {_, Val}} -> Val; false -> 0 end. nformat(A1, A2, A3, A4, A5, A6) -> io:format("~-20s ~-7s ~-6s ~8s ~8s ~s~n", [A1,A2,A3,A4,A5,A6]). print_info(Node) -> case node_info(Node) of {ok, Info} -> State = fetch(state, Info), In = fetch(in, Info), Out = fetch(out, Info), Type = fetch(type, Info), Address = fmt_address(fetch(address, Info)), io:format("Node = ~p~n" "State = ~p~n" "Type = ~p~n" "In = ~p~n" "Out = ~p~n" "Address = ~s~n", [Node, State, Type, In, Out, Address]); Error -> Error end. verbose(Term, Level, #state{verbose = Verbose}) when Verbose >= Level -> error_logger:info_report({net_kernel, Term}); verbose(_, _, _) -> ok. getnode(P) when pid(P) -> node(P); getnode(P) -> P. From etxuwig@REDACTED Fri Apr 20 11:44:00 2001 From: etxuwig@REDACTED (Ulf Wiger) Date: Fri, 20 Apr 2001 11:44:00 +0200 (MET DST) Subject: Hierarchical Distribution In-Reply-To: Message-ID: On Thu, 19 Apr 2001, Vance Shipley wrote: >Folks, > >I came across Ericsson's 'Cello' product recently and found the >description of it's "execution platform" quite interesting. I >am left wondering if it might be Erlang/OTP based. Cello is based on the OSE/Delta RTOS (http://www.ose.com) If you think OSE resembles Erlang in many ways, it's probably because they have much in common in terms of ancestry. As I've heard it, the OSE OS was originally much based on ideas from EriPascal. The person responsible for the creation of EriPascal was one Bjarne D?cker... /Uffe -- Ulf Wiger tfn: +46 8 719 81 95 Senior System Architect mob: +46 70 519 81 95 Strategic Product & System Management ATM Multiservice Networks Data Backbone & Optical Services Division Ericsson Telecom AB From bjarne@REDACTED Fri Apr 20 14:55:37 2001 From: bjarne@REDACTED (Bjarne =?iso-8859-1?Q?D=E4cker?=) Date: Fri, 20 Apr 2001 14:55:37 +0200 Subject: Erlang/OTP User Conference, Stockholm, September 27, 2001 Message-ID: <3AE031C9.B9A62C4B@erix.ericsson.se> Dear Erlang Friends, Your are all invited to this year's EUC which will take place in Stockholm on September 27: http://www.erlang.se/euc/01/ The conference fee is 1200 SEK. This is also a Call-for-papers and we are looking forward to papers both on technical aspects and on application experiences. Best wishes Bjarne Computer Science Laboratory Ericsson Utvecklings AB PO Box 1505 S-125 25 ?lvsj? - Stockholm Sweden From rickard.green@REDACTED Fri Apr 20 15:04:33 2001 From: rickard.green@REDACTED (Rickard Green) Date: Fri, 20 Apr 2001 15:04:33 +0200 Subject: Hierarchical Distribution References: <20010420092124W.mbj@bluetail.com> Message-ID: <3AE033E1.620AC1C6@uab.ericsson.se> Hidden nodes and hidden global groups will be included in the next r7b01 patch (which will be released soon). The following is cut from the coming manual page for erl(1): --8<-- -hidden Starts the Erlang system as a hidden node if the system is run as a distributed node. Hidden nodes always establish hidden connections to all other nodes except for nodes in the same global group. Hidden connections aren't published on neither of the connected nodes, i.e. neither of the connected nodes are part of the result from nodes/0 on the other node. See also hidden global groups, global_group(3). -->8-- and global_group(3): --8<-- The global_groups-key in the .config file defines the global groups: {global_groups, [GroupTuple]} Types: GroupTuple = {GroupName, [Node]} | {GroupName, PublishType, [Node]} GroupName = atom() (naming a global group) PublishType = normal | hidden Node = atom() (naming a node) A GroupTuple without PublishType is the same as a GroupTuple with PublishType equal to normal. -->8-- --8<-- A hidden node will establish hidden connections to nodes not part of the same global group, and normal (visible) connections to nodes part of the same global group. Hidden connections aren't published on neither of the connected nodes, i.e. neither of the connected nodes are part of the result from nodes/0 on the other node. In a hidden global group (a global group defined with PublishType equal to hidden) all nodes are hidden nodes. Hidden nodes can also be part of normal global groups. Nodes started with the -hidden switch will be hidden nodes even if they are part of a normal group, see erl(1). Other nodes in the group will not be affected by this. -->8-- /Rickard Green, Ericsson UAB, Erlang/OTP Martin Bjorklund wrote: > > "Vance Shipley" wrote: > > > The logical topology uses a 'main processor cluster' (MPC). The > > BPs are hierarchically under a single MP while the SPs are hierarchically > > under a single BP. > > > > +----------------------------+ > > | MPC +----+ | +----+ +----+ > > | | MP | |------| BP | -----| SP | > > | +----+ /| +----+ / +----+ > > | +----+ +----+ / | +----+/ +----+ > > | | MP | | MP |<--|------| BP |-------| SP | > > | +----+ +----+ \ | +----+\ +----+ > > | \| +----+ \ +----+ > > | +----+ |------| BP | -----| SP | > > | | MP | | +----+ +----+ > > | +----+ | > > +----------------------------+ > > > > So in distribution terms BPs only know about a single MP, and possibly > > some SPs. Each MP knows about all other MPs but only knows about it's > > own BPs, and I guess not any SPs. > > > > Can the standard net_kernel be configured in this way? > > This is how our system works at bluetail, in order to make it > scalable. We have a main cluster (which we call master cluster) of > 2-4 master nodes. This is normal erlang distribution; fully connected > net, the mnesia tables are replicated on these nodes etc. Then we > have up to 252 slave nodes (like the BPs in the pic above). They are > so called 'hidden nodes'. The reason for doing this is scalability; > we really don't want a fully connected net of 256 nodes with mnesia > actively replicating everything... > > Now, the net_kernel in R7B cannot handle 'hidden nodes'. It does > handle 'hidden connections' though. A hidden node is a distributed > erlang node which won't show up in the nodes() list. But it's got a > node name, and you can use normal erlang rpc and so on. > > Hidden nodes is a very powerful concept (we also use them for several > other things), which I think should be part of the standard release. > I'll include the modified files below. > > Start a hidden node like this: > > erl -sname xxx -kernel hidden true > > A hidden node only accepts hidden connection attempts. This means > that a hidden node can connect to another hidden node, like SP to BP > in the picture above. Normally, the hidden node connects to the > normal node, although a normal node can do a 'hidden connect' in order > to connect to a hidden node. > > Try this by starting a hidden node, and a normal node. Do a net:ping() > from the hidden node to the normal node. Do net_kernel:i() to print > the connections in both nodes. Also check erlang:nodes(). > > /martin > > ------------------------------------------------------------------------ > Name: dist_util.erl > dist_util.erl Type: Plain Text (Text/Plain) > Encoding: 7bit > > Name: net_kernel.erl > net_kernel.erl Type: Plain Text (Text/Plain) > Encoding: 7bit From jrcv196@REDACTED Mon Apr 23 00:33:25 2001 From: jrcv196@REDACTED (James Vernon) Date: Sun, 22 Apr 2001 23:33:25 +0100 (BST) Subject: Erlang Scheduler: what does it do? Message-ID: Hi, Can someone tell me how Erlang processes are scheduled? I'm doing some quick research into real-time languages, I know Erlang is only a `soft' real-time system, but just how soft is it? Does Erlang make any attempt at all to meet real-time deadlines other than making context switches very fast and efficient? I'm supposed to be giving a presentation on my research, so any info on why Erlang is a great language for RTS, would be much appreciated :) but the scheduling is the thing that baffles me at the moment. Cheers, James Vernon From mickael.remond@REDACTED Mon Apr 23 10:56:10 2001 From: mickael.remond@REDACTED (Mickael Remond) Date: Mon, 23 Apr 2001 10:56:10 +0200 Subject: Web server and Erlang promotion Message-ID: <20010423105610.A6865@idealx.com> Hello, These days I have read several articles on Caudium, a web server developped in the Pikes Language. >From what I understand, the Caudium web server is now used as a way to promote the language. The Caudium Web server has very nice features and can compete with Apache. The best way and easiest way to extend it is to develop Pikes code. What would you think of trying to boost Inets to turn it into a competing Web server that can be extended in Erlang to promote the language ? When people will try the web server, they will probably also try module development. Then, they will probably try Erlang and they might be happy with the code developped in Erlang. -- Micka?l R?mond From etxuwig@REDACTED Mon Apr 23 11:25:59 2001 From: etxuwig@REDACTED (Ulf Wiger) Date: Mon, 23 Apr 2001 11:25:59 +0200 (MET DST) Subject: Erlang Scheduler: what does it do? In-Reply-To: Message-ID: On Sun, 22 Apr 2001, James Vernon wrote: >Hi, > >Can someone tell me how Erlang processes are scheduled? I'm doing >some quick research into real-time languages, I know Erlang is only >a `soft' real-time system, but just how soft is it? Does Erlang make >any attempt at all to meet real-time deadlines other than making >context switches very fast and efficient? Erlang processes are currently scheduled on a reduction count basis. One reduction is roughly equivalent to a function call. A process is allowed to run until it pauses to wait for input (a message from some other process) or until it has executed 1000 reductions. There are functions to slightly optimize the scheduling of a process (yield(), bump_reductions(N)), but they are only meant for very restricted use, and may be removed if the scheduler changes. A process waiting for a message will be re-scheduled as soon as there is something new in the message queue, or as soon as the receive timer (receive ... after Time -> ... end) expires. It will then be put last in the appropriate queue. Erlang has 4 scheduler queues (priorities): 'max', 'high', 'normal', and 'low'. 'max' and 'high' are strict. This means that the scheduler will first look at the 'max' queue and run processes there until the queue is empty; then it will do the same for the 'high' queue. 'normal' and 'low' are fair priorities. Assuming no processes at levels 'max' and 'high', the scheduler will run 'normal' processes until the queue is empty, or until it has executed a total of 8000 reductions (8*Max_Process_Reds); then it will execute one 'low' priority process, if there is one ready to run. The relationship between 'normal' and 'low' introduces a risk of priority inversion. If you have hundreds of (active) 'normal' processes, and only a few 'low', the 'low' processes will actually get higher priority than the 'normal' processes. There was an experimental version of a multi-pro Erlang runtime system. It supported Erlang processes as OS threads, and then used the scheduling and prority levels of the underlying OS. One important thing to note about the scheduling is that from the programmer's perspective, the following three things are invariant: - scheduling is preemptive. - a process is suspended if it enters a receive statement, and there is no matching message in the message queue. - if a receive timer expires, the process is rescheduled. The Erlang Processor, for example, will most likely schedule based on CPU cycles (essentially a time-based scheduler). It may also have multiple thread pools, allowing it to context switch within one clock cycle to another process if the active process has to e.g. wait for memory. In a runtime environment that supports it, it should be possible to also have erlang processes at interrupt priority (meaning that they will be allowed to run as soon as there is something for them to do -- not having to wait until a 'normal' priority process finishes its timeslice.) /Uffe -- Ulf Wiger tfn: +46 8 719 81 95 Senior System Architect mob: +46 70 519 81 95 Strategic Product & System Management ATM Multiservice Networks Data Backbone & Optical Services Division Ericsson Telecom AB From enano@REDACTED Mon Apr 23 14:02:24 2001 From: enano@REDACTED (Miguel Barreiro Paz) Date: Mon, 23 Apr 2001 14:02:24 +0200 (CEST) Subject: CFP: SI+T 2001 Message-ID: I've been asked to forward the SI+T call for papers, in case someone is interested. SI+T 2001 --------- Symposium on Informatics and Telecommunications September 12-14, 2001, A Coru?a, SPAIN Under the auspices of DINTEL: Foundation for the spreading of the Informatics and Telecommunication Engineering This Symposium follows the former Spanish Symposium on Distributed Computing (SEID 1999 and SEID 2000) - now aiming towards better reflecting the current converging trends and even synergy among Information Technology and Telecommunications, where the distributed nature becomes an inherent part of many research results, developments and innovations in Computer Science, all of which has been hugely potentiated by the widespread Internet phenomenon. Scope: SI+T 2001 welcomes those articles that introduce original Research + Development + Engineering results and achieve, through the integration or joint use of Computing and Telecommunications, a significative added value. Not trying to be exhaustive, interest topics for SI+T 2001 include: Pervasive Computing, Mobile Computing, Teleworking, Distance Learning, Telemedicine, Teledetection, Telerobotics, Mobile Agents, Network Intelligence, Internet Software Engineering, Online Distributed Systems, Cluster Computing, Telecommunications Databases, Realtime Systems and Security. "DINTEL Foundation" Best Papers Awards First: funded with SIX HUNDRED THOUSAND PESETAS (3606 Euros), and Diploma Second: funded with THREE HUNDRED THOUSAND PESETAS (1803 Euros), and Diploma Third: funded with ONE HUNDRED THOUSAND PESETAS (601 Euros), and Diploma Submissions: Authors are invited to send full papers. Electronic submissions will be compulsory. Papers should be limited to 4500 words (about 12 pages). If authors believe that more details are necessary, they may include a clearly marked appendix which will be read at the discretion of the referee. Each paper should also contain a short abstract of approximately 200 words. If available, e-mail addresses and fax numbers of the authors should also be included. The Conference Proceedings will be published by DINTEL Foundation. A commitment that the paper will be presented at the conference by one of the authors is a pre-condition for an accepted paper to be included in the proceedings. We encourage the authors to use LaTeX article style. Papers should be submitted in PostScript or PDF format, and should be readable by Adobe's Acrobat Reader. Important Dates: Deadline for Submission: May 31, 2001 Notification to Authors: June 30, 2001 Final Version of Accepted Papers due on: July 15, 2001 Address: J. L. Freire Computer Science Department Faculty of Informatics University of A Coru?a 15071 A Coru?a, SPAIN E-mail: sit01@REDACTED Fax: +34-981167160 Phone: +34-981167000 Electronic Submission and further information: http://www.lfcia.org/sit01 From thomasl@REDACTED Wed Apr 25 17:34:07 2001 From: thomasl@REDACTED (Thomas Lindgren) Date: Wed, 25 Apr 2001 14:34:07 -0100 Subject: Alternative compiler messages Message-ID: <200104251534.f3PFY7P01461@lammgam.bluetail.com> Here's an alternative way of reporting compiler errors and warnings that I have found useful. What does it do? - Filename is reported once, rather than per compiler message. This reduces clutter when absolute paths are used; - Warnings about unused functions are summarized into a single line, rather than one line per function; - Errors about undefined functions are reported once per undef.function (giving all the lines at once), rather than once per line. What do you have to do? Save the file below somewhere as: compile2.erl and compile it: erlc -I/lib/stdlib/include compile2.erl Now you can try compile2:file(File) with some rotten File and see whether you like the results. If so, rename the module to 'compile' and move the file (after saving the original compile.erl, if you're being careful) into: /lib/compiler/src/compile.erl Make, and make install. You're set. Enjoy, and let me know about any bugs (none known), Thomas -- Thomas Lindgren thomas+junk@REDACTED Alteon WebSystems ---------------------------------------------------------------------- %% ``The contents of this file are subject to the Erlang Public License, %% Version 1.1, (the "License"); you may not use this file except in %% compliance with the License. You should have received a copy of the %% Erlang Public License along with this software. If not, it can be %% retrieved via the world wide web at http://www.erlang.org/. %% %% Software distributed under the License is distributed on an "AS IS" %% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See %% the License for the specific language governing rights and limitations %% under the License. %% %% The Initial Developer of the Original Code is Ericsson Utvecklings AB. %% Portions created by Ericsson are Copyright 1999, Ericsson Utvecklings %% AB. All Rights Reserved.'' %% %% $Id$ %% %% Purpose: Run the Erlang compiler. -module(compile2). -include("erl_compile.hrl"). %% High-level interface. -export([file/1,file/2,format_error/1,iofile/1,compile/3]). -export([forms/1,forms/2]). -export([output_generated/1]). -export([options/0,options/1]). %% Internal functions. -export([internal/3]). -import(lists, [member/2,reverse/1,keysearch/3,last/1, map/2,foreach/2,foldr/3,any/2,filter/2]). -define(DEF_VERSION, v3). %Default compiler version. %% file(FileName) %% file(FileName, Options) %% Compile the module in file FileName. -define(DEFAULT_OPTIONS, [verbose,report_errors,report_warnings]). -define(pass(P), {P,fun P/1}). file(File) -> file(File, ?DEFAULT_OPTIONS). file(File, Opts) when atom(Opts) -> file(File, [Opts|?DEFAULT_OPTIONS]); file(File, Opts) when list(Opts) -> do_compile({file,File}, Opts++env_default_opts()). forms(File) -> forms(File, ?DEFAULT_OPTIONS). forms(Forms, Opts) when atom(Opts) -> forms(Forms, [Opts|?DEFAULT_OPTIONS]); forms(Forms, Opts) when list(Opts) -> do_compile({forms,Forms}, [binary|Opts++env_default_opts()]). env_default_opts() -> Key = "ERL_COMPILER_OPTIONS", case os:getenv(Key) of false -> []; Str when list(Str) -> case erl_scan:string(Str) of {ok,Tokens,_} -> case erl_parse:parse_term(Tokens ++ [{dot, 1}]) of {ok,List} when list(List) -> List; {ok,Term} -> [Term]; {error,Reason} -> io:format("Ignoring bad term in ~s\n", [Key]), [] end; {error, {_,_,Reason}, _} -> io:format("Ignoring bad term in ~s\n", [Key]), [] end end. do_compile(Input, Opts0) -> Opts = foldr(fun expand_opt/2, [], Opts0), Serv = spawn_link(?MODULE, internal, [self(),Input,Opts]), receive {Serv,Rep} -> Rep end. %% Given a list of compilation options, returns true if compile:file/2 %% would have generated a Beam file, false otherwise (if only a binary or a %% listing file would have been generated). output_generated(Opts) -> any(fun ({save_binary,F}) -> true; (Other) -> false end, passes(file, ?DEF_VERSION, Opts)). expand_opt(report, Os) -> [report_errors,report_warnings|Os]; expand_opt(return, Os) -> [return_errors,return_warnings|Os]; expand_opt(O, Os) -> [O|Os]. %% format_error(ErrorDescriptor) -> string() format_error(jam_is_dead) -> "JAM is dead!"; format_error(v1_is_dead) -> "The v1 compiler is no longer supported."; format_error(v2_is_dead) -> "The v2 compiler is no longer supported."; format_error({open,E}) -> io_lib:format("open error '~s'", [file:format_error(E)]); format_error(write_error) -> "error writing file"; format_error({rename,S}) -> io_lib:format("error renaming ~s", [S]); format_error({parse_transform,M,R}) -> io_lib:format("error in transform '~s': ~p", [M, R]); format_error({crash,Pass,Reason}) -> io_lib:format("internal error in ~p;\ncrash reason: ~p", [Pass,Reason]); format_error({bad_return,Pass,Reason}) -> io_lib:format("internal error in ~p;\nbad return value: ~p", [Pass,Reason]); format_error({unused_functions,[{F,A}|Fs]}) -> %% note: Fs is non-empty list; see s_warn/N below for when this warning is %% generated io_lib:format("functions ~p/~p~s are unused.", [F,A,lists:flatten( [io_lib:format(", ~p/~p",[F,A]) || {F,A} <- Fs ])]); format_error({undefined_function,{F,A},[Line]}) -> io_lib:format("line ~w: function ~w/~w undefined.",[Line,F,A]); format_error({undefined_function,{F,A},Lines}) -> io_lib:format("lines ~w: function ~w/~w undefined.",[Lines,F,A]). %% The compile state record. -record(compile, {filename="", dir="", base="", ifile="", ofile="", module=[], code=[], abstract_code=[], %Abstract code for debugger. options=[], errors=[], warnings=[]}). internal(Master, Input, Opts) -> Master ! {self(), case catch internal(Input, Opts) of {'EXIT', Reason} -> {error, Reason}; Other -> Other end}. internal({forms,Forms}, Opts0) -> {Ver,Opts} = compiler_version(Opts0), Ps = passes(forms, Ver, Opts), internal_comp(Ps, "", "", #compile{code=Forms,options=Opts}); internal({file,File}, Opts0) -> {Ver,Opts} = compiler_version(Opts0), Ps = passes(file, Ver, Opts), case member(asm, Opts) of false -> internal_comp(Ps, File, ".erl", #compile{options=Opts}); true -> internal_comp(Ps, File, ".S", #compile{options=Opts}) end. internal_comp(Passes, File, Suffix, St0) -> Dir = filename:dirname(File), Base = filename:basename(File, Suffix), St1 = St0#compile{filename=File, dir=Dir, base=Base, ifile=erlfile(Dir, Base, Suffix), ofile=objfile(Base, St0)}, Run = case member(time, St1#compile.options) of true -> fun run_tc/2; false -> fun({Name,Fun}, St) -> catch Fun(St) end end, case fold_comp(Passes, Run, St1) of {ok,St2} -> comp_ret_ok(St2); {error,St2} -> comp_ret_err(St2) end. fold_comp([{Name,Pass}|Ps], Run, St0) -> case Run({Name,Pass}, St0) of {ok,St1} -> fold_comp(Ps, Run, St1); {error,St1} -> {error,St1}; {'EXIT',Reason} -> Es = [{St0#compile.ifile,[{none,?MODULE,{crash,Name,Reason}}]}], {error,St0#compile{errors=St0#compile.errors ++ Es}}; Other -> Es = [{St0#compile.ifile,[{none,?MODULE,{bad_return,Name,Other}}]}], {error,St0#compile{errors=St0#compile.errors ++ Es}} end; fold_comp([], Run, St) -> {ok,St}. os_process_size() -> case os:type() of {unix, sunos} -> Size0 = os:cmd("ps -o vsz -p " ++ os:getpid() ++ " | tail -1"), Size = list_to_integer(lib:nonl(Size0)); _ -> 0 end. run_tc(NameFun, St) -> run_tc(NameFun, statistics(runtime), St). run_tc({Name,Fun}, Before0, St) -> %% This division into two functions is a hack. If we would had stack %% trimming, dead variables would have been removed from the stack. %% Well, anyway, the St variable will not be saved on the stack, %% because it is not referenced after the catch. Val = (catch Fun(St)), After0 = statistics(runtime), {Before_c, _} = Before0, {After_c, _} = After0, io:format(" ~-30s: ~10.3f s (~w k)\n", [Name, (After_c-Before_c) / 1000, os_process_size()]), Val. comp_ret_ok(St) -> report_warnings(St), Ret1 = case member(binary, St#compile.options) of true -> [St#compile.code]; false -> [] end, Ret2 = case member(return_warnings, St#compile.options) of true -> Ret1 ++ [St#compile.warnings]; false -> Ret1 end, list_to_tuple([ok,St#compile.module|Ret2]). comp_ret_err(St) -> report_errors(St), report_warnings(St), case member(return_errors, St#compile.options) of true -> {error,St#compile.errors,St#compile.warnings}; false -> error end. %% passes(form|file, [Option]) -> [{Name,PassFun}] %% Figure out which passes that need to be run. passes(forms, Ver, Opts) -> select_passes(standard_passes(Ver), Opts); passes(file, Ver, Opts) -> Ps = case member(asm, Opts) of true -> [?pass(beam_consult_asm),?pass(beam_asm), {unless,binary,?pass(save_binary)}]; false -> [?pass(parse_module)|standard_passes(Ver)] end, Fs = select_passes(Ps, Opts), %% If the last pass saves the resulting binary to a file, %% insert a first pass to remove the file. [?pass(error_if_jam)| case last(Fs) of {save_binary,Fun} -> [?pass(remove_file)|Fs]; Other -> Fs end]. %% select_passes([Command], Opts) -> [{Name,Function}] %% Interpret the lists of commands to return a pure list of passes. %% %% Command can be one of: %% %% {pass,Mod} Will be expanded to a call to the external %% function Mod:module(Code, Options). This %% function must transform the code and return %% {ok,NewCode} or {error,Term}. %% Example: {pass,beam_codegen} %% %% {Name,Fun} Name is an atom giving the name of the pass. %% Fun is an 'fun' taking one argument: a compile record. %% The fun should return {ok,NewCompileRecord} or %% {error,NewCompileRecord}. %% Note: ?pass(Name) is equvivalent to {Name, fun Name/1}. %% Example: ?pass(parse_module) %% %% {src_listing,Ext} Produces an Erlang source listing with the %% the file extension Ext. (Ext should not contain %% a period.) No more passes will be run. %% %% {listing,Ext} Produce an listing of the terms in the internal %% representation. The extension of the listing %% file will be Ext. (Ext should not contain %% a period.) No more passes will be run. %% %% {iff,Flag,Cmd} If the given Flag is given in the option list, %% Cmd will be interpreted as a command. %% Otherwise, Cmd will be ignored. %% Example: {iff,dcg,{listing,"codegen}} %% %% {unless,Flag,Cmd} If the given Flag is NOT given in the option list, %% Cmd will be interpreted as a command. %% Otherwise, Cmd will be ignored. %% Example: {unless,no_kernopt,{pass,sys_kernopt}} %% select_passes([{pass,Mod}|Ps], Opts) -> F = fun(St) -> case catch Mod:module(St#compile.code, St#compile.options) of {ok,Code} -> {ok,St#compile{code=Code}}; {error,Es} -> {error,St#compile{errors=St#compile.errors ++ Es}} end end, [{Mod,F}|select_passes(Ps, Opts)]; select_passes([{src_listing,Ext}|Ps], Opts) -> [{listing,fun (St) -> src_listing(Ext, St) end}]; select_passes([{listing,Ext}|Ps], Opts) -> [{listing,fun (St) -> listing(Ext, St) end}]; select_passes([{iff,Flag,Pass}|Ps], Opts) -> select_cond(Flag, true, Pass, Ps, Opts); select_passes([{unless,Flag,Pass}|Ps], Opts) -> select_cond(Flag, false, Pass, Ps, Opts); select_passes([{Name,Fun}|Ps], Opts) when function(Fun) -> [{Name,Fun}|select_passes(Ps, Opts)]; select_passes([], Opts) -> []; select_passes([List|Ps], Opts) when list(List) -> Nested = select_passes(List, Opts), case last(Nested) of {listing,Fun} -> Nested; Other -> Nested ++ select_passes(Ps, Opts) end. select_cond(Flag, ShouldBe, Pass, Ps, Opts) -> ShouldNotBe = not ShouldBe, case member(Flag, Opts) of ShouldBe -> select_passes([Pass|Ps], Opts); ShouldNotBe -> select_passes(Ps, Opts) end. %% The standard passes (almost) always run. standard_passes(v1) -> [?pass(v1_is_dead)]; standard_passes(v2) -> [?pass(v2_is_dead)]; standard_passes(v3) -> [?pass(transform_module), {iff,'P',{src_listing,"P"}}, ?pass(lint_module), %% Note: erl_lint only warns for obviously unused functions, not %% for self-recursive functions never called. %% ?pass(remove_unused_functions), ?pass(expand_module), {iff,dexp,{listing,"expand"}}, {iff,'E',{src_listing,"E"}}, {iff,'abstr',{listing,"abstr"}}, {iff,debug_info,?pass(save_abstract_code)}, %% Core Erlang passes. {pass,v3_core}, {iff,dcore,{listing,"core"}}, {unless,no_copt,{pass,v3_core_opt}}, {iff,dcopt,{listing,"coreopt"}}, {iff,clint,?pass(core_lint_module)}, %% Kernel Erlang and code generation. {pass,v3_kernel}, {iff,dkern,{listing,"kernel"}}, {pass,v3_life}, {iff,dlife,{listing,"life"}}, {pass,v3_codegen}, {iff,dcg,{listing,"codegen"}}, %% Assembly level optimisations. {unless,no_postopt, [{pass,beam_block}, {iff,dblk,{listing,"block"}}, {pass,beam_bs}, {iff,dbs,{listing,"bs"}}, {unless,no_jopt,{pass,beam_jump}}, {iff,djmp,{listing,"jump"}}, {unless,no_topt,{pass,beam_type}}, {iff,dtype,{listing,"type"}}, {pass,beam_flatten}]}, {iff,dopt,{listing,"optimize"}}, {iff,'S',{listing,"S"}}, ?pass(beam_asm), {unless,binary,?pass(save_binary)}]. compiler_version(Opts) -> compiler_version(Opts, []). compiler_version([O|Opts], Acc) -> case is_compiler_version(O) of true -> {O,[O|[Opt || Opt <- Opts, not is_compiler_version(Opt)]++Acc]}; false -> compiler_version(Opts, [O|Acc]) end; compiler_version([], Acc) -> {?DEF_VERSION,[?DEF_VERSION|Acc]}. is_compiler_version(v1) -> true; is_compiler_version(v2) -> true; is_compiler_version(v3) -> true; is_compiler_version(_) -> false. %%% %%% Compiler passes. %%% %% Remove the target file so we don't have an old one if the compilation fail. remove_file(St) -> file:delete(St#compile.ofile), {ok,St}. -record(asm_module, {module, exports, labels, functions=[], cfun, code}). preprocess_asm_forms(Forms) -> R = #asm_module{}, R1 = collect_asm(Forms, R), {R1#asm_module.module, {R1#asm_module.module, R1#asm_module.exports, R1#asm_module.functions, R1#asm_module.labels}}. collect_asm([], R) -> case R#asm_module.cfun of undefined -> R; {A,B,C} -> R#asm_module{functions=R#asm_module.functions++ [{function,A,B,C,R#asm_module.code}]} end; collect_asm([{module,M} | Rest], R) -> collect_asm(Rest, R#asm_module{module=M}); collect_asm([{exports,M} | Rest], R) -> collect_asm(Rest, R#asm_module{exports=M}); collect_asm([{labels,M} | Rest], R) -> collect_asm(Rest, R#asm_module{labels=M}); collect_asm([{function,A,B,C} | Rest], R) -> R1 = case R#asm_module.cfun of undefined -> R; {A0,B0,C0} -> R#asm_module{functions=R#asm_module.functions++ [{function,A0,B0,C0,R#asm_module.code}]} end, collect_asm(Rest, R1#asm_module{cfun={A,B,C}, code=[]}); collect_asm([X | Rest], R) -> collect_asm(Rest, R#asm_module{code=R#asm_module.code++[X]}). beam_consult_asm(St) -> case file:consult(St#compile.ifile) of {ok, Forms0} -> {Module, Forms} = preprocess_asm_forms(Forms0), {ok,St#compile{module=Module, code=Forms}}; {error,E} -> Es = [{St#compile.ifile,[{none,compile,{open,E}}]}], {error,St#compile{errors=St#compile.errors ++ Es}} end. error_if_jam(St) -> case member(jam, St#compile.options) of true -> Es = [{St#compile.ifile,[{none,compile,jam_is_dead}]}], {error,St#compile{errors=St#compile.errors ++ Es}}; false -> {ok,St} end. v1_is_dead(St) -> Es = [{St#compile.ifile,[{none,compile,v1_is_dead}]}], {error,St#compile{errors=St#compile.errors ++ Es}}. v2_is_dead(St) -> Es = [{St#compile.ifile,[{none,compile,v2_is_dead}]}], {error,St#compile{errors=St#compile.errors ++ Es}}. parse_module(St) -> Opts = St#compile.options, Cwd = case keysearch(cwd, 1, Opts) of {value, {cwd, Dir}} -> Dir; _ -> "." end, IncludePath = [Cwd, St#compile.dir|inc_paths(Opts)], case epp:parse_file(St#compile.ifile, IncludePath, pre_defs(Opts)) of {ok,Forms} -> {ok,St#compile{code=Forms}}; {error,E} -> Es = [{St#compile.ifile,[{none,compile,{open,E}}]}], {error,St#compile{errors=St#compile.errors ++ Es}} end. compile_options([{attribute,L,compile,C}|Fs]) when list(C) -> C ++ compile_options(Fs); compile_options([{attribute,L,compile,C}|Fs]) -> [C|compile_options(Fs)]; compile_options([_F|Fs]) -> compile_options(Fs); compile_options([]) -> []. transforms(Os) -> [ M || {parse_transform,M} <- Os ]. transform_module(St) -> %% Extract compile options from code into options field. Ts = transforms(St#compile.options ++ compile_options(St#compile.code)), foldl_transform(St, Ts). foldl_transform(St, [T|Ts]) -> Name = "transform " ++ atom_to_list(T), Fun = fun(S) -> T:parse_transform(S#compile.code, S#compile.options) end, Run = case member(time, St#compile.options) of true -> fun run_tc/2; false -> fun({N,F}, S) -> catch F(S) end end, case Run({Name, Fun}, St) of {'EXIT',R} -> Es = [{St#compile.ifile,[{none,compile,{parse_transform,T,R}}]}], {error,St#compile{errors=St#compile.errors ++ Es}}; Forms -> foldl_transform(St#compile{code=Forms}, Ts) end; foldl_transform(St, []) -> {ok,St}. %%% Fetches the module name from a list of forms. The module attribute must %%% be present. get_module([{attribute,_,module,M} | _]) -> M; get_module([_ | Rest]) -> get_module(Rest). %%% A #compile state is returned, where St.base has been filled in %%% with the module name from Forms, as a string, in case it wasn't %%% set in St (i.e., it was ""). add_default_base(St, Forms) -> F = St#compile.filename, case F of "" -> M = get_module(Forms), St#compile{base = atom_to_list(M)}; _ -> St end. lint_module(St) -> case erl_lint:module(St#compile.code, St#compile.ifile, St#compile.options) of {ok,Ws} -> %% Insert name of module as base name, if needed. This is %% for compile:forms to work with listing files. St1 = add_default_base(St, St#compile.code), {ok,St1#compile{warnings=St1#compile.warnings ++ Ws}}; {error,Es,Ws} -> {error,St#compile{warnings=St#compile.warnings ++ Ws, errors=St#compile.errors ++ Es}} end. core_lint_module(St) -> case core_lint:module(St#compile.code, St#compile.options) of {ok,Ws} -> {ok,St#compile{warnings=St#compile.warnings ++ Ws}}; {error,Es,Ws} -> {error,St#compile{warnings=St#compile.warnings ++ Ws, errors=St#compile.errors ++ Es}} end. %% remove_unused_functions(State) -> State' %% Remove any local functions not called in the module, based on the warnings %% generated by erl_lint. %% See comment in standard_passes/1. % remove_unused_functions(St0) -> % Eds = flatmap(fun({F,Ws}) -> Ws end, St0#compile.warnings), % case [{Func,Arity} || {L,erl_lint,{not_called,{Func,Arity}}} <- Eds] of % [] -> {ok,St0}; % NotCalled -> % Code = filter(fun({function,L,N,A,Cs}) -> not member({N,A}, NotCalled); % (Other) -> true end, % St0#compile.code), % {ok,St0#compile{code=Code}} % end. %% expand_module(State) -> State' %% Do the common preprocessing of the input forms. expand_module(St0) -> {Mod,Exp,Forms,Opts} = sys_pre_expand:module(St0#compile.code, St0#compile.options), {ok,St0#compile{module=Mod,options=Opts,code={Mod,Exp,Forms}}}. save_abstract_code(St) -> {ok,St#compile{abstract_code=abstract_code(St)}}. abstract_code(#compile{code={Mod,Exp,Forms}}) -> Abstr = {abstract_v1,Forms}, case catch erlang:term_to_binary(Abstr, [compressed]) of {'EXIT',_} -> term_to_binary(Abstr); Other -> Other end. beam_asm(#compile{code=Code0,abstract_code=Abst,options=Opts0}=St) -> Opts = filter(fun is_informative_option/1, Opts0), case beam_asm:module(Code0, Abst, Opts) of {ok,Code} -> {ok,St#compile{code=Code,abstract_code=[]}}; {error,Es} -> {error,St#compile{errors=St#compile.errors ++ Es}} end. %% Returns true if the option is informative and therefore should be included %% in the option list of the compiled module. is_informative_option(beam) -> false; is_informative_option(report_warnings) -> false; is_informative_option(report_errors) -> false; is_informative_option(binary) -> false; is_informative_option(verbose) -> false; is_informative_option(_) -> true. save_binary(St) -> Tfile = tmpfile(St#compile.ofile), %Temp working file case write_binary(Tfile, St#compile.code, St) of ok -> case file:rename(Tfile, St#compile.ofile) of ok -> {ok,St}; {error,E} -> file:delete(Tfile), Es = [{St#compile.ofile,[{none,?MODULE,{rename,Tfile}}]}], {error,St#compile{errors=St#compile.errors ++ Es}} end; {error,E} -> Es = [{Tfile,[{compile,write_error}]}], {error,St#compile{errors=St#compile.errors ++ Es}} end. write_binary(Name, Bin, St) -> Opts = case member(compressed, St#compile.options) of true -> [compressed]; false -> [] end, case file:open(Name, [write, raw|Opts]) of {ok, Fd} -> Res = case file:write(Fd, Bin) of ok -> ok; {error, Reason} -> {error, Reason} end, file:close(Fd), Res; {error, Reason} -> {error, Reason} end. %% report_errors(State) -> ok %% report_warnings(State) -> ok report_errors(St) -> case member(report_errors, St#compile.options) of true -> foreach(fun ({{F,L},Eds}) -> list_errors(F, Eds); ({F,Eds}) -> list_errors(F, Eds) end, St#compile.errors); false -> ok end. report_warnings(St) -> case member(report_warnings, St#compile.options) of true -> foreach(fun ({{F,L},Eds}) -> list_warnings(F, Eds); ({F,Eds}) -> list_warnings(F, Eds) end, St#compile.warnings); false -> ok end. %% ftl -- extras list_errors(F,Errs) -> list_errors2(F,Errs). list_warnings(F,Warnings) -> list_warnings2(F,Warnings). %% list_errors(File, ErrorDescriptors) -> ok list_errors1(F, [{Line,Mod,E}|Es]) -> io:fwrite("~s:~w: ~s\n", [F,Line,apply(Mod, format_error, [E])]), list_errors1(F, Es); list_errors1(F, [{Mod,E}|Es]) -> io:fwrite("~s: ~s\n", [F,apply(Mod, format_error, [E])]), list_errors1(F, Es); list_errors1(F, []) -> ok. %% list_warnings(File, ErrorDescriptors) -> ok list_warnings1(F, [{Line,Mod,E}|Es]) -> io:fwrite("~s:~w: Warning: ~s\n", [F,Line,apply(Mod, format_error, [E])]), list_warnings1(F, Es); list_warnings1(F, [{Mod,E}|Es]) -> io:fwrite("~s: Warning: ~s\n", [F,apply(Mod, format_error, [E])]), list_warnings1(F, Es); list_warnings1(F, []) -> ok. %% ftl -- better (shorter, clearer) error and warning listings list_errors2(F,[]) -> ok; list_errors2(F,Errs) -> io:fwrite("*** ERRORS ~s:~n",[F]), Errors = summarize_errors(Errs), lists:foreach( fun({Line,Mod,E}) -> io:fwrite("line ~w: ~s~n",[Line,Mod:format_error(E)]); ({Mod,E}) -> io:fwrite("~s~n",[Mod:format_error(E)]) end, Errors). list_warnings2(F,[]) -> ok; list_warnings2(F, Warns0) -> io:fwrite("WARNINGS ~s:~n",[F]), Warnings = summarize_warnings(Warns0), lists:foreach( fun({Line,Mod,E}) -> io:fwrite("line ~w: Warning: ~s~n",[Line,Mod:format_error(E)]); ({Mod,E}) -> io:fwrite("Warning: ~s~n",[Mod:format_error(E)]) end, Warnings). %% collapse some errors into one (eg, all undefined_function errors) %% %% Ordinary: untreated errors %% UDs: undefined functions, [{{F,A},Lines}] summarize_errors(Errs) -> s_err(Errs,[],[]). s_err([X|Xs],Ordinary,UDs) -> case X of {Line,Mod,{undefined_function,FA}} -> s_err(Xs,Ordinary,insert_line(FA,Line,UDs)); _ -> s_err(Xs,[X|Ordinary],UDs) end; s_err([],Ordinary,UDs) -> Ordinary ++ undefined_function_errors(UDs). %% insert another line error for function F/A insert_line(FA,Line,[{FA,Lines}|Xs]) -> %% append lines to get correct order at once (we could keep the list %% reversed and reverse it at error-print time) [{FA,Lines ++ [Line]}|Xs]; insert_line(FA,Line,[X|Xs]) -> [X|insert_line(FA,Line,Xs)]; insert_line(FA,Line,[]) -> [{FA,[Line]}]. %% convert {FA,[Line]} into real error info %% - Note: currently, we ALWAYS print a list of error lines, even if the %% list has length 1. This could be prettified. %% - The _new_ undefined_function error is defined in this module, rather %% than in erl_lint; we should perhaps give it a new name? undefined_function_errors(Errs) -> [ {?MODULE,{undefined_function,FA,Lines}} || {FA,Lines}<- Errs ]. %% collapse warnings about unused functions etc into a single extended %% warning (see also format_error/1 in this file, which handles the %% new error) %% summarize_warnings(Warns) -> s_warn(Warns,[],[]). %% s_warn: summarize warnings %% %% Ordinary: untreated warnings %% UFs: unused function warnings, reversed order %% %% returns new list of warnings s_warn([X|Xs],Ordinary,UFs) -> case X of {Line,Mod,{unused_function,{F,A}}} -> s_warn(Xs,Ordinary,[X|UFs]); _ -> s_warn(Xs,[X|Ordinary],UFs) end; s_warn([],Ordinary,UFs) -> UF_warns = unused_function_warnings(UFs), Ordinary ++ UF_warns. %% unused_function_warnings(UFs) -> if length(UFs) < 2 -> %% too few unused functions to bother UFs; true -> [{?MODULE, {unused_functions, lists:reverse( [ {F,A} || {Line,Mod,{unused_function,{F,A}}} <- UFs ])}}] end. %% erlfile(Dir, Base) -> ErlFile %% outfile(Base, Extension, Options) -> OutputFile %% objfile(Base, Target, Options) -> ObjFile %% tmpfile(ObjFile) -> TmpFile %% Work out the correct input and output file names. iofile(File) when atom(File) -> iofile(atom_to_list(File)); iofile(File) -> {filename:dirname(File), filename:basename(File, ".erl")}. erlfile(Dir, Base, Suffix) -> filename:join(Dir, Base++Suffix). outfile(Base, Ext, Opts) when atom(Ext) -> outfile(Base, atom_to_list(Ext), Opts); outfile(Base, Ext, Opts) -> Obase = case keysearch(outdir, 1, Opts) of {value, {outdir, Odir}} -> filename:join(Odir, Base); Other -> Base % Not found or bad format end, Obase++"."++Ext. objfile(Base, St) -> outfile(Base, "beam", St#compile.options). tmpfile(Ofile) -> reverse([$#|tl(reverse(Ofile))]). %% pre_defs(Options) %% inc_paths(Options) %% Extract the predefined macros and include paths from the option list. pre_defs([{d,M,V}|Opts]) -> [{M,V}|pre_defs(Opts)]; pre_defs([{d,M}|Opts]) -> [M|pre_defs(Opts)]; pre_defs([O|Opts]) -> pre_defs(Opts); pre_defs([]) -> []. inc_paths(Opts) -> [ P || {i,P} <- Opts, list(P) ]. src_listing(Ext, St) -> listing(fun (Lf, {Mod,Exp,Fs}) -> do_src_listing(Lf, Fs); (Lf, Fs) -> do_src_listing(Lf, Fs) end, Ext, St). do_src_listing(Lf, Fs) -> foreach(fun (F) -> io:put_chars(Lf, [erl_pp:form(F),"\n"]) end, Fs). listing(Ext, St) -> listing(fun(Lf, Fs) -> beam_listing:module(Lf, Fs) end, Ext, St). listing(LFun, Ext, St) -> Lfile = outfile(St#compile.base, Ext, St#compile.options), case file:open(Lfile, [write]) of {ok,Lf} -> LFun(Lf, St#compile.code), ok = file:close(Lf), {ok,St}; {error,E} -> Es = [{Lfile,[{none,compile,write_error}]}], {error,St#compile{errors=St#compile.errors ++ Es}} end. options() -> help(standard_passes(?DEF_VERSION)). %% Intentionally undocumented. options(Version) -> help(standard_passes(Version)). help([{iff,Flag,{src_listing,Ext}}|T]) -> io:fwrite("~p - Generate .~s source listing file\n", [Flag,Ext]), help(T); help([{iff,Flag,{listing,Ext}}|T]) -> io:fwrite("~p - Generate .~s file\n", [Flag,Ext]), help(T); help([{iff,Flag,{Name,Fun}}|T]) when function(Fun) -> io:fwrite("~p - Run ~s\n", [Flag,Name]), help(T); help([{iff,Flag,Action}|T]) -> help(Action), help(T); help([{unless,Flag,{pass,Pass}}|T]) -> io:fwrite("~p - Skip the ~s pass\n", [Flag,Pass]), help(T); help([{unless,no_postopt=Flag,List}|T]) when list(List) -> %% Hard-coded knowledgde here. io:fwrite("~p - Skip all post optimisation\n", [Flag]), help(List), help(T); help([{unless,Flag,Action}|T]) -> help(Action), help(T); help([H|T]) -> help(T); help(_) -> ok. %% compile(AbsFileName, Outfilename, Options) %% Compile entry point for erl_compile. compile(File, _OutFile, Options) -> case file(File, make_erl_options(Options)) of {ok, _Mod} -> ok; Other -> Other end. %% Converts generic compiler options to specific options. make_erl_options(Opts) -> %% This way of extracting will work even if the record passed %% has more fields than known during compilation. Includes0 = Opts#options.includes, Defines = Opts#options.defines, Outdir = Opts#options.outdir, Warning = Opts#options.warning, Verbose = Opts#options.verbose, Specific = Opts#options.specific, Optimize = Opts#options.optimize, OutputType = Opts#options.output_type, Cwd = Opts#options.cwd, Includes = case Opts#options.ilroot of undefined -> Includes0; Ilroot -> [Ilroot|Includes0] end, Options = case Verbose of true -> [verbose]; false -> [] end ++ case Warning of 0 -> []; _ -> [report_warnings] end ++ case Optimize of 0 -> [no_kernopt,no_postopt]; 1 -> [no_postopt]; Other -> [] end ++ map( fun ({Name, Value}) -> {d, Name, Value}; (Name) -> {d, Name} end, Defines) ++ case OutputType of undefined -> []; jam -> [jam]; beam -> [beam] end, Options++[report_errors, {cwd, Cwd}, {outdir, Outdir}| map(fun(Dir) -> {i, Dir} end, Includes)]++Specific. From rv@REDACTED Wed Apr 25 15:09:30 2001 From: rv@REDACTED (Robert Virding) Date: Wed, 25 Apr 2001 15:09:30 +0200 Subject: Alternative compiler messages In-Reply-To: Your message of "Wed, 25 Apr 2001 14:34:07 -0100." <200104251534.f3PFY7P01461@lammgam.bluetail.com> Message-ID: <200104251309.PAA29753@trana.bluetail.com> Thomas Lindgren writes: > >Here's an alternative way of reporting compiler errors and warnings >that I have found useful. What does it do? > >- Filename is reported once, rather than per compiler message. This > reduces clutter when absolute paths are used; > >- Warnings about unused functions are summarized into a single line, > rather than one line per function; > >- Errors about undefined functions are reported once per undef.function > (giving all the lines at once), rather than once per line. The original way of reporting errors was chosen so as to be compatible with CC and GCC. The rationale being it would be what people were used to and would (hopefully) work under Emacs when using the commands to step over errors. The other two may be beneficial but the grouping should probably be done in erl_lint where the errors are generated. These changes, and others for presenting compile info, could be added to the standard system if some form of consensus is reached. so please try it and come with an opinion, Robert From qtxkleb@REDACTED Thu Apr 26 07:39:23 2001 From: qtxkleb@REDACTED (Bengt Kleberg) Date: Thu, 26 Apr 2001 07:39:23 +0200 (MET DST) Subject: Alternative compiler messages Message-ID: <200104260539.HAA25657@avc280.etxb.ericsson.se> > From: Thomas Lindgren > To: erlang-questions@REDACTED > Subject: Alternative compiler messages > > > Here's an alternative way of reporting compiler errors and warnings > that I have found useful. What does it do? > > - Filename is reported once, rather than per compiler message. This > reduces clutter when absolute paths are used; While the rest is absolutly marvelous (and something I use 'sed' to do myself) there is a problem (for me) with this particular feature. My user interface/editor uses the : information to open the file at that line with 1 mouse click. Therefore I need the filename right beside the line number to be able to get to the right place to fix the problems. Would you consider making it possible to turn on/off the different features individually? Bengt Kleberg From Bita.A.Nazemian@REDACTED Thu Apr 26 14:40:52 2001 From: Bita.A.Nazemian@REDACTED (Bita Nazemian A (QTX)) Date: Thu, 26 Apr 2001 14:40:52 +0200 Subject: Colour Message-ID: <0F8184664EA9D21192F70008C75D16920B2EB545@esealnt145> Hi ! Where can I find different background colours that can be used in Erlang. / From thomasl@REDACTED Thu Apr 26 17:48:19 2001 From: thomasl@REDACTED (Thomas Lindgren) Date: Thu, 26 Apr 2001 14:48:19 -0100 Subject: Alternative compiler messages In-Reply-To: <200104260539.HAA25657@avc280.etxb.ericsson.se> (message from Bengt Kleberg on Thu, 26 Apr 2001 07:39:23 +0200 (MET DST)) References: <200104260539.HAA25657@avc280.etxb.ericsson.se> Message-ID: <200104261548.f3QFmJc01360@lammgam.bluetail.com> > While the rest is absolutly marvelous (and something I use 'sed' to do > myself) there is a problem (for me) with this particular feature. My > user interface/editor uses the : information to > open the file at that line with 1 mouse click. Therefore I need the > filename right beside the line number to be able to get to the right > place to fix the problems. Will this still work for summarized undefined functions? Ie, if things look like myfile.erl:[100,102,104]: function foo/2 is not defined. Will your emacs mode work for those inputs? > Would you consider making it possible to turn on/off the different > features individually? Sure, that's straightforward. I'll look at it. Best, Thomas -- Thomas Lindgren thomas+junk@REDACTED Alteon WebSystems From qtxkleb@REDACTED Thu Apr 26 14:58:46 2001 From: qtxkleb@REDACTED (Bengt Kleberg) Date: Thu, 26 Apr 2001 14:58:46 +0200 (MET DST) Subject: Alternative compiler messages Message-ID: <200104261258.OAA26610@avc280.etxb.ericsson.se> > From thomasl@REDACTED Thu Apr 26 14:51:11 2001 > > While the rest is absolutly marvelous (and something I use 'sed' to do > > myself) there is a problem (for me) with this particular feature. My > > user interface/editor uses the : information to > > open the file at that line with 1 mouse click. Therefore I need the > > filename right beside the line number to be able to get to the right > > place to fix the problems. > > Will this still work for summarized undefined functions? Ie, if things > look like > > myfile.erl:[100,102,104]: function foo/2 is not defined. > > Will your emacs mode work for those inputs? This is not emacs. It is wily (acme). But anyway: No, I need: myfile.erl:100: function foo/2 is not defined. BTW: Has anybody manged to get the new pretty printer (as in syntax_tools-1.0) to read from stdin? I manage by creating a tmp file, but I would rather not. Bengt Kleberg From klacke@REDACTED Thu Apr 26 15:53:09 2001 From: klacke@REDACTED (Klacke) Date: Thu, 26 Apr 2001 15:53:09 +0200 Subject: Colour In-Reply-To: <0F8184664EA9D21192F70008C75D16920B2EB545@esealnt145>; from Bita.A.Nazemian@etx.ericsson.se on Thu, Apr 26, 2001 at 02:40:52PM +0200 References: <0F8184664EA9D21192F70008C75D16920B2EB545@esealnt145> Message-ID: <20010426155308.C9424@bluetail.com> > > Where can I find different background colours that can be used in Erlang. > Yeah .... at last I get a reason to submit a little lib I wrote sometime ago to the erlang contrib site. It's an erlang interface to the slang libraries and I just sent it to contrib@REDACTED slang is a tty lib which is used to implement some of my favourite applications (one is the MUA i'm using just right now, mutt) And ... it can be used to "set the background colours" Cheers /klacke -- Claes Wikstrom -- Caps lock is nowhere and Alteon WebSystems -- everything is under control http://www.bluetail.com/~klacke -- From jabba@REDACTED Fri Apr 27 21:08:55 2001 From: jabba@REDACTED (Jani Launonen) Date: Fri, 27 Apr 2001 22:08:55 +0300 (EEST) Subject: Problem setting DefaultQoS with Notification Service Message-ID: Hello everyone, I've quite new to Erlang and haven't had the time to learn it properly as I'm in bit haste to set up OTP's Notification Service in place of JacORB's Event Service. So no Erlang is yet involved in our code. There's an example use of Notification Service at page http://www.erlang.org/doc/r7b/lib/cosNotification-1.0.2/doc/html/ch_example.html#7.1 which has this code: %% Create a new event channel. Ch = 'CosNotifyChannelAdmin_EventChannelFactory': create_channel(ChFac, DefaultQoS, DefaultAdmin), The question is, whether there is these DefaultQoS and DefaultAdmin defined somewhere or are these just put as an place holders here? Or is there a way to create channel with default QoS/Admin values by some other function? Page http://www.erlang.org/doc/r7b/lib/cosNotification-1.0.2/doc/html/ch_QoS.html#6 does say, what the default values are, so these must be hidden somewhere in the source. I've tried to find out this DefaultQoS (DefaultAdmin) (or similar) in the source, but failed so far. Could someone help me with this one as I'm bit confused with the record syntax in Erlang (and especially those Corba mappings) and haven't succeeded creating default values by myself. As a bonus question, I'm interested if the Notification Service is automatically load balanced across Erlang nodes. Can Orber domain used somehow to distribute load across nodes and machines. You know, I'd smile like idiot if I could make it run with bunch of Sun E450:s :-). We have some friendly competition with our VTT (Natinal Research Centre) researchers in scalability. Thank you. -+-+-+- Jani Launonen Home: (08) 8802 180/Work: +358 8 553 2810 jabba@REDACTED Paalikatu 14 A 410 90520 OULU Student. . . . . . . . . .University of Oulu, Dept. of EE Assistant Researcher . . .Genie of the Net Project From andy@REDACTED Fri Apr 27 23:32:02 2001 From: andy@REDACTED (Andy with Recycled Electrons) Date: Fri, 27 Apr 2001 16:32:02 -0500 Subject: Erlang Hacker For Hire Message-ID: Greetings, my fellow Erlang hackers; It seems that in Ericsson's downsizing of 20,000 recently, they even hit one Erlang hacker (me). If anyone is hiring an Erlang hacker, who loves embedded and real time systems, please let me know. My resume is at: http://www.Andy-Allen.com I've been hacking AXD-301 Maintenance and Service pages for about 4-1/2 months, and just finished my Masters of Science in Computer Science Engineering. My thesis was posted on www.Erlang.org (it's now under "old news") and is available at http://www.MercuryRobot.com Thanks! Andy Allen Andy@REDACTED -------------- next part -------------- An HTML attachment was scrubbed... URL: From jabba@REDACTED Mon Apr 30 02:28:34 2001 From: jabba@REDACTED (Jani Launonen) Date: Mon, 30 Apr 2001 03:28:34 +0300 (EEST) Subject: Problem setting DefaultQoS with Notification Service In-Reply-To: Message-ID: Hello everyone, previously I had problems setting QoS-parametres - that is now passed. After trying to circumvent the problem by using java to call EventChannelFactory:create_channel (which didn't succeed by some weird problem with JacORB's idl-compiler), I used cut & paste -method from the sources and after reading little bit manuals and tutorials I could construct a kind of working record which was accepted by EventChannelFactory:create_channel. Jesh! There only seems to be problem with JacORB's understanding of IORs with "wstring inside Anys" - even after a patch to recent JacORB 1.3.21. This is off topic but just to tell the status that no one is going to bother with the previous problem. >[cut introduction] > >There's an example use of Notification Service at page >http://www.erlang.org/doc/r7b/lib/cosNotification-1.0.2/doc/html/ch_example.html#7.1 >which has this code: > > %% Create a new event channel. > Ch = 'CosNotifyChannelAdmin_EventChannelFactory': > create_channel(ChFac, DefaultQoS, DefaultAdmin), > >The question is, whether there is these DefaultQoS and DefaultAdmin >defined somewhere or are these just put as an place holders here? Or is >there a way to create channel with default QoS/Admin values by some other >function? Page >http://www.erlang.org/doc/r7b/lib/cosNotification-1.0.2/doc/html/ch_QoS.html#6 >does say, what the default values are, so these must be hidden somewhere in >the source. I've tried to find out this DefaultQoS (DefaultAdmin) (or >similar) in the source, but failed so far. Could someone help me with this >one as I'm bit confused with the record syntax in Erlang (and especially >those Corba mappings) and haven't succeeded creating default values by myself. > >[cut bonus question] Cheers, -+-+-+- Jani Launonen Home: (08) 8802 180/Work: +358 8 553 2810 jabba@REDACTED Paalikatu 14 A 410 90520 OULU Student. . . . . . . . . .University of Oulu, Dept. of EE Assistant Researcher . . .Genie of the Net Project