Hacker News new | past | comments | ask | show | jobs | submit login
JPL C Coding Standard [pdf] (nasa.gov)
79 points by c0rtex on Sept 2, 2015 | hide | past | web | favorite | 32 comments



> There shall be no direct or indirect use of recursive function calls.

> [The] absence of recursion prevents runaway code, and helps to secure predictable performance for all tasks.

I can see the reasoning here, but wow, banning recursion..


That sorta goes hand-in-hand with "There shall be no use of dynamic memory allocation after task initialization." Which in my experience is the harder rule to deal with. It's not that often that you really truly need recursion. It happens, just not often. But no allocations, that radically changes how one codes.

But, these rules are for safety on embedded systems with very small amounts of memory. These represent the best lessons learned, the best ways to guarantee your function will finish in finite time and not run out of memory.


"But no allocations, that radically changes how one codes."

But this does not forbid implementing ones own memory manager that uses blocks from a huge continuous buffer.


True. Though I don't work at JPL or speak to what they would allow or forbid, but if you did implement your own memory manager, I imagine that you'd still need to write code that guarantees it will never run out, which might be harder to do if you have this abstraction. The spirit of these rules is to be enable code writing that is obviously correct and finite in time and space, at a glance.

So I'm in pure speculation land here, but I could see JPL frowning on rolling your own memory manager. Though I also imagine many applications of these rules happen on hardware that is small enough to make writing a memory manager overkill anyway.


Well, when your code runs on an interplanetary spacecraft, you tend to be extra cautious.


Yes, these guidelines do not dictate how ground software, and especially research codes, are written at JPL. See the section of the doc under "Scope."



Here's a great video with a bit more background about why you'd want to adopt such a standard.

https://www.usenix.org/conference/hotdep12/workshop-program/...


> The goto statement shall not be used...

> ...often results in improved code clarity

Um not really, if goto is used _right_ it clarifies code immensely.


They're avoiding the "not used right" problem in flight control software.

The full quote is "Simpler control flow translates into stronger capabilities for both human and tool-based analysis and often results in improved code clarity. Mission critical code should not just be arguably, but trivially correct."

So there are reasons beyond code clarity.

Seems reasonable to me.


Gotos are trivial. They map directly to unconditional jumps at the assembly level.

They're one of the best ways to implement exception handling in C (look at how the Linux kernel uses them).

It's also not unusual to have conditionals that don't nest. Of course, if you really want to avoid using a goto, you have the option of duplicating a lot of code in order to satisfy all the code paths, decreasing readability and making it harder to maintain. Or you can just use a goto and not worry about it.

I do understand why people want to avoid 'goto', but Dijkstra wasn't always right...


I was at JPL when we were developing these standards. C was still pretty new there, they used FORTRAN almost exclusively before that.

In practice, this rule had little impact. The only time you could justify using a goto was for cleanup, and you could always end-run around the rule.

    ... blah blah ...
    if (error)
       goto fail;
    ... blah blah ...
    if (error)
       goto fail;
    ... blah blah ...

  fail:
    ... cleanup ...
just gets turned into

  do {
    ... blah blah ...
    if (error)
        break;
    ... blah blah ...
    if (error)
        break;
    ... etc...
  } while (0);
  if (error) {
    ... cleanup ...
  }


If the compiler in question does tail call optimization (or even if not), maybe one could write a cleanup function that takes the place of the goto block, but this looks pretty ugly with all the parameters:

    int cleanup(void *ptr1, char *ptr2, struct foo *ptr3, int *ptr4, union xyz *ptr5, int ret)
    {
      free(ptr1);
      // etc.
      return ret;
    }

    // later...
      if(fail1) {
        return cleanup(a, b, c, d, e, -1);
      }


The advantage of gotos is clear next to the alternatives. First, the dreadful pyramid style:

  err = funcA(&resourceA)
  if (succeeded(err)) {

      err = funcB(&resourceB)
      if (succeeded(err)) {

          err = funcC(&resourceC)
          if (succeeded(err)) {

              ...and so forth...

              cleanup(resourceC)
          }
          
          free(resourceB)
          
      }
      specialFree(resourceA);
  }

  return err;
  
A new indentation level for every single function that can fail or return a resource. Hard to read and hard to edit.

Even worse is duplicating the cleanup code:

  err = funA(&resourceA)
  if (failed(err)) {
      specialFree(resourceA);
      return err;
  }

  err = funB(&resourceB)
  if (failed(err)) {
     free(resourceB);
     specialFree(resourceA);
     return err;
  }

  err = funC(&resourceC)
  if (failed(err)) {
      cleanup(resourceC);
      free(resourceB);
      specialFree(resourceA);
      return err;
  }

  ...and so forth...
Now with gotos:

     err = funA(&resourceA);
     if (failed(err))
         goto Error;
  
     err = funB(&resourceB);
     if (failed(err))
         goto Error;
  
     err = funC(&resourceC);
     if (failed(err))
         goto Error;
  
   Error:
     if (resourceA)
         specialFree(resourceA);
  
     if (resourceB)
         free(resourceB);
  
     if (resourceC)
         cleanup(resourceB);
  
     free err;
Much better. If you use the same error code type across the project, define a macro for "if (failed(err)) goto Error;" to save the keystrokes and lines-of-code. Some projects prefer to use a different goto label for each exit point, somewhat in lieu of ifs. The advantage of using ifs is that you can usually be loose about the order of resource cleanup.

This use of goto is widespread and highly structured, so tools should be able to handle it too.


Still seems like a needless use of goto.

    struct resources {
        resourceA *a;
        resourceB *b;
        resourceC *c;
    };
    
    int init(resources *r) {
        err = funA(r->a);
        if (err)
            {cleanup(r); return err;}
        
        err = funB(r->b);
        if (err)
            {cleanup(r); return err;}
        
        err = funC(r->c);
        if (err)
            {cleanup(r); return err;}
        
        return 0;
    }
    
    void cleanup(resources *r) {
        if (r->a)
            {specialFree(r->a);}
        
        if (r->b)
            {specialFree(r->b);}
        
        if (r->c)
            {specialFree(r->c);}
        
        free r;
     }
And this has the added benefit of being more structured, more testable. The same cleanup() code can be used for normal cleanup as well as error handling, so you have less duplication.


This doesn't have any advantages over the goto version. So now you're defining a struct and a special cleanup function for every function that you write. And in the name of what? A religious avoidance of goto?


> A religious avoidance of goto?

That's my experience.

In school the no goto rule is exactly like your second grade teacher telling you never to start a sentence with the word 'and' It's to prevent this; And we went to the park. And I played on the swing And then we went and got ice cream. And we went home and had dinner.

The no goto rule is for novices to teach them not to write spaghetti code.


>The JPL recommended static analyzers are fast, and produce sparse and accurate messages.

I'm interested in what analyzers they may be recommending. I think this is a field that's come a long way since 2009.

A lot of these standards could actually be implemented as rules in modern products, which would assist with using them.


The lead author of this standard is Gerard Holzmann, a Fellow of the ACM and a member of the NAE. He works at JPL, and has been instrumental in introducing code analysis to flight software (e.g., http://lars-lab.jpl.nasa.gov).

The analyzers JPL currently uses are Coverity, Semmle, CodeSonar, and Klocwork. Coverity seems most common.

Of course, Gerard was an author of Spin (http://spinroot.com/spin/whatispin.html).


It's lovely reading a coding standards document that is 100% focused on code safety, and has zero stylistic argument.

These rules are a good reminder for me, too. A lot of them were habit when I was doing C/C++ on game consoles. Using more dynamic scripting languages on machines with more and more memory over the years has helped me forget. But most, if not all, of these rules have valid analogues in any language that can be aspired to.


Why were parts of the standard omitted? Money, national security, or both?


According to the document itself - copyright. LOC-5 and LOC-6 can't be reproduced because they are copyright MISRA, and Appendix A can't be reproduced because it is copyright ISO. Both documents are publicly available to anyone willing to pay for them. So national security / government secrecy doesn't appear to be a reason here.


Yes.


I was hoping there would be a mention of always using metric units.


Anyone happens to have a link to MISRA-C PDF ? :)


It's not expensive: http://www.misra.org.uk/Buyonline/tabid/58/Default.aspx

I sympathise with people who complain about ISO and friends charging mega-bucks for standards, but MISRA's prices seem rather reasonable.


The sad part about standards is that there are (at least) two type of standards: absolutely necessary to guarantee interoperability (think: nuts and bolts and other types of hardware, networking, software, electrical characteristics of components etc) and the ones that usually are regulatory requirements, things like ISO27000 or ISO9000. And then there are absolutely open (and free) standards.

Standards that are necessary for basic interoperability should be all free (at least in the software space, maybe that's why I see standards that get "promoted" from IETF RFC-s and other less bureaucratic sources to ISO as regressions). And the ones that are usually used in regulatory processes, can be paid for, as those who insist on or desire conformance usually have monetary incentives involved.

Now, charging for "standards" that contribute to "public safety and security" (what MANY C programs are, for sure) is something that does not fit into either category.

But true, the MISRA prices are reasonable (if the content is indeed worth any money for purposes other than compliance).


I hope you realize your asking people to publicly commit a crime. Not making any value judgements, I just wanted to make sure you're aware.


Last time I googled, I ended up with nothing. This time it works much better: google:"filetype:pdf MISRA-C" and hitting "I'm feeling lucky" takes me to somewhere where is a PDF.

I would not bet that I've committed a crime by searching the web for information (what the web is for), I think that where I live (northern part of EU) this is an OK thing to do.


Breaking the law, sure, but crime? Simple copyright infrineent tends not to be criminal. Other stuff is meeded - like selling the copyright material.


The colloquial use of the word crime means an action that breaks a law.


No, the colloquial use doesn't include stuff for which you can't be punished by the state. It's not a colloquialism, it's just an error.




Guidelines | FAQ | Support | API | Security | Lists | Bookmarklet | Legal | Apply to YC | Contact

Search: