Returned Value versus Exception Handling

Most software developers now a day write code using object oriented (OO) programming languages. In some cases, due to performance reasons, some code may be written using a non OO language. One way or the other, the question may come up if return codes are better than using exceptions. I do not believe you can come with enough reasons to justify one method or the other which would be accepted by most software developers. What I will do is discuss some considerations and give my opinion. Please take it all with a grain of salt.

Let’s first take a look at “returned value” and agree on what it is and how it works.

When one writes a function or method it may return void (nothing) or an integer with an error code. The reason for this is to inform the caller if something out of the ordinary occurred while executing. This concept was made popular several decades ago in UNIX (not Linux which came decades after). If a command executed and returned a value of zero (0) all was well; otherwise it would return a non-zero code. I believe that today most command line interfaces (CLIs) in most platforms follow such convention (at least I do).

My interpretation of this convention when developing code is to return zero when all goes well, a negative number when an issue is encountered (information) and a positive value when the code encountered an error (failure). Given this is a convention the entire development team should follow it. I always hear the complaint that perhaps not everyone will adhere to it so one is exposed to unexpected issues. If that is the case, you have larger problems in your team. You need to conduct code inspections. At that point issues like not following a convention should be detected and addressed.

I have also heard that using returned values create problems when developers use -1 for warning, 0 for all is well and 1 for error. The codes need to explain what the warning / error is about. In addition, you have to consider translating the codes to different natural languages. Allow me to show you some C/C++ code:

LIBRARY_FUNCTION_HEADER	QueIsFull	(
									SENCOR_QUEUE	*queue,
									BOOL			*isFull
									)

//	***************************************************************@@
//	- This function checks if the specified queue is full.
//	*****************************************************************

{

int		retVal;										// value returned by this function

// **** initialization ****
retVal = WAR_SUCCESS;								// hope all goes well

// **** inform what is going on ****
if (traceExecution > 1)
	EventLog(EVENT_INFO,
	"QueIsFull <<< ENTERING line: %d\n",
	__LINE__);

// **** perform sanity checks ****
if (queue == (SENCOR_QUEUE *)NULL)
	{
	EventLog(EVENT_ERROR,
	"QueIsFull <<< queue == NULL line: %d file ==>%s<==\n",
	__LINE__, __FILE__);
	retVal = WAR_INVALID_ARGUMENT;					// flag the problem
	goto done;										// to perform clean up
	}

if (isFull == (BOOL *)NULL)
	{
	EventLog(EVENT_ERROR,
	"QueIsFull <<< isFull == NULL line: %d file ==>%s<==\n", __LINE__, __FILE__); retVal = WAR_INVALID_ARGUMENT; // flag the problem goto done; // to perform clean up } // **** check if the queue has NOT been initialized **** if ((queue->next == (SENCOR_QUEUE *)NULL) ||
	(queue->prev == (SENCOR_QUEUE *)NULL))
	{
	EventLog(EVENT_ERROR,
	"QueIsFull <<< Queue might NOT have been initialized next: 0x%08x prev: 0x%08x line: %d file ==>%s<==\n", queue->next, queue->prev, __LINE__, __FILE__);
	retVal = WAR_OBJECT_NOT_INITIALIZED;			// flag the problem
	goto done;										// to perform clean up
	}

// **** update the default count (if needed) ****
if (queue->maxCount == (unsigned long)0L)
	queue->maxCount = QUEUE_DEFAULT_COUNT;

// **** determine if the queue is full or not ****
if (queue->count >= queue->maxCount)
	{
	*isFull = (BOOL)(1 == 1);						// queue is full
	}
else
	{
	*isFull = (BOOL)(0 == 1);						// queue is NOT full
	}

// **** clean up ****
done:

// **** log what is going on ****
if (traceExecution > 1)								// do NOT check the returned value
	EventLog(EVENT_INFO,
	"QueIsFull <<< retVal: %d line: %d file ==>%s<==\n",
	retVal, __LINE__, __FILE__);

// **** inform caller what went on ****
return retVal;
}

The QueIsFull() function / method is one of the fifty or so functions in the queue class. The code was written for speed using the C programming language. Today you can find several libraries that implement queues. This library was written some time ago when FOSS (Free and Open Source Software) libraries where not popular or available. That said; the library contains many features that would not be available in most FOSS offerings.

The function declares a retVal integer used to return a code to the caller indicating what went on in the function while executing. Please keep in mind that all issues encountered by all functions / methods are logged in order for support and development engineers be able to exactly determine what went on.

As you will see, in this project, errors and warnings are grouped together (the code returns 0 or a negative value). The following is a listing of the warning codes:

<<<                     WAR_NO_MORE_DATA (-1001) No more data available / found
<<<               WAR_CONNECTION_REFUSED (-1002) Socket connection refused
<<<                     WAR_SOCK_TIMEOUT (-1003) Attempt to connect TIMED OUT without establishing a connection
<<<                WAR_CONNECTION_CLOSED (-1004) Socket connection gracefully closed by client
<<<              WAR_CONNECTION_TIMEDOUT (-1005) Socket connection timed out
<<<                  WAR_INVALID_COMMAND (-1006) Invalid command
<<<             WAR_SOCK_WOULD_NOT_BLOCK (-1007) Specified socket would NOT block
<<<                   WAR_FILE_NOT_FOUND (-1008) Specified file or path could NOT be found
<<<                 WAR_BUFFER_TOO_SMALL (-1009) Buffer is too small
<<<                   WAR_PATH_NOT_FOUND (-1010) Specified path could NOT be found
<<<                     WAR_LOGIN_FAILED (-1011) Login operation failed
<<<                     WAR_IOCTL_FAILED (-1012) IOCTL call failed
<<<               WAR_INCORRECT_FUNCTION (-1013) Incorrect function
<<<                 WAR_DEVICE_NOT_READY (-1014) Device NOT ready
<<<                        WAR_CRC_ERROR (-1015) Cyclic Redundancy Check (CRC) error
<<<                WAR_NO_MEDIA_IN_DRIVE (-1016) No media in drive
<<<                     WAR_END_OF_MEDIA (-1017) End Of Media (EOM)
<<<                         WAR_NO_LABEL (-1018) No label in media
<<<               WAR_MEDIA_OUT_OF_ORDER (-1019) Media out of order in library
<<<                 WAR_OPERATION_FAILED (-1020) The operation failed
<<<             WAR_SCSI_CHECK_CONDITION (-1021) SCSI check condition
<<<                 WAR_INVALID_PASSWORD (-1022) Invalid user name or password
<<<                   WAR_FILE_NOT_READY (-1023) File NOT ready
<<<                      WAR_DEVICE_FULL (-1024) The device is full
<<<              WAR_TOO_MANY_SEMA_POSTS (-1025) Too many posts to the semaphore
<<<              WAR_NO_MEDIA_IN_LIBRARY (-1026) No more media in library or stacker
<<<               WAR_CONNECTION_ABORTED (-1027) Socket connection aborted
<<<                 WAR_DUPLICATE_RECORD (-1028) Duplicate record
<<<                 WAR_RECORD_NOT_FOUND (-1029) Record NOT found
<<<                 WAR_CONNECTION_RESET (-1030) Socket connection reset by the remote side (caller should close socket)
<<<                   WAR_NO_EMPTY_DRIVE (-1031) No empty drives in library
<<<                WAR_NO_MIGRATION_INFO (-1032) No migration information
<<<                  WAR_INVALID_LICENSE (-1033) Invalid OR expired software license
<<<             WAR_SERVER_SHUTTING_DOWN (-1034) Computer system shutting down
<<<        WAR_NO_IDLE_VOLUMES_IN_DRIVES (-1035) No idle volume(s) in drive(s)
<<<               WAR_NO_AVAILABLE_DRIVE (-1036) No drive(s) available
<<<         WAR_DEVICE_REQUIRES_CLEANING (-1037) Drive requires cleaning
<<<                  WAR_NOT_IMPLEMENTED (-1038) Operation NOT implemented
<<<             WAR_MEDIA_NOT_RECORDABLE (-1039) Media NOT recordable
<<<                 WAR_WRONG_MEDIA_TYPE (-1040) Unexpected media type
<<<        WAR_OPERATION_ABORTED_BY_USER (-1041) Operation aborted by user
<<<                WAR_OPERATION_TIMEOUT (-1042) Operation timeout
<<<                    WAR_INVALID_VALUE (-1043) Invalid value or value NOT supported yet
<<<              WAR_OPERATION_ABANDONED (-1044) Operation abandoned
<<<                  WAR_FILE_NOT_COPIED (-1045) File NOT copied
<<<                     WAR_NULL_POINTER (-1046) NULL pointer
<<<                 WAR_NOT_A_VALID_PATH (-1047) Not a valid (incorrect syntax) file path
<<<                   WAR_INVALID_HANDLE (-1048) Invalid handle
<<<                 WAR_INVALID_ARGUMENT (-1049) Unexpected argument
<<<           WAR_INVALID_ARGUMENT_VALUE (-1050) Unexpected argument value
<<<                   WAR_INTERNAL_ERROR (-1051) Internal issue. Check with technical support.
<<<                   WAR_NO_MORE_MEMORY (-1052) System out of memory
<<<                  WAR_NETWORK_FAILURE (-1053) Network related failure
<<<       WAR_OPERATION_FAILED_IN_SERVER (-1054) Operation failed on server
<<<            WAR_READ_OPERATION_FAILED (-1055) Read operation failed
<<<           WAR_WRITE_OPERATION_FAILED (-1056) Write operation failed
<<<                  WAR_NOT_ENOUGH_DATA (-1057) Not enough data
<<<                   WAR_CANCEL_BY_USER (-1058) Operation cancelled by user
<<<                  WAR_NO_MORE_VOLUMES (-1059) No more volumes
<<<       WAR_NO_IDLE_OR_AVAILABLE_DRIVE (-1060) No idle or available drive
<<<           WAR_DRIVE_BUSY_FOR_REQUEST (-1061) Drive busy for pending request(s)
<<<                 WAR_INCORRECT_VOLUME (-1062) Incorrect volume
<<<                    WAR_TOO_MANY_HOPS (-1063) Bitfile request has hopped too many times
<<<                  WAR_LIBRARY_IS_FULL (-1064) No more free space in automated library
<<<       WAR_NO_REQUEST_TO_BE_PROCESSED (-1065) No request found to process
<<<                WAR_FILE_BEING_COPIED (-1066) File being copied
<<<              WAR_FILE_ALREADY_COPIED (-1067) File already copied
<<<           WAR_VOLUME_NOT_INITIALIZED (-1068) Volume NOT initialized
<<<            WAR_DB_OPERATION_DISABLED (-1069) Database operation disabled
<<<         WAR_TOO_MANY_FILES_TO_RELOAD (-1070) Too many files to reload
<<<                WAR_NOTHING_TO_RELOAD (-1071) No bitfiles found to reload
<<<                 WAR_DUPLICATE_VOLUME (-1072) This is a duplicate volume
<<<       WAR_FILE_NEED_TO_BE_DUPLICATED (-1073) File need to be duplicated
<<<            WAR_CONFIG_RECORD_MISSING (-1074) Missing configuration record in database
<<<               WAR_OPERATION_DISABLED (-1075) Operation disabled
<<<       WAR_LICENSED_CAPACITY_EXCEEDED (-1076) System licensed capacity exceeded
<<<             WAR_INVALID_PRODUCT_NAME (-1077) Unexpected product name
<<<                WAR_CHECKSUM_MISMATCH (-1078) Checksum mismatch
<<<          WAR_BITFILE_TRAILER_MISSING (-1079) Bitfile does NOT have a bitfile trailer
<<<                   WAR_BITFILE_IN_USE (-1080) Bitfile in use
<<<                    WAR_EMPTY_BITFILE (-1081) Empty bitfile (size == 0)
<<<                     WAR_INVALID_MOVE (-1082) Unexpected move volume operation
<<<               WAR_TREES_DO_NOT_MATCH (-1083) Trees do NOT match (files or folders missing)
<<<                 WAR_BITFILE_OFF_LINE (-1084) Bitfile off-line
<<<                       WAR_PICK_ERROR (-1085) Failed to pick disc (from bin, drive or tray)
<<<           WAR_INCONSISTENT_LIB_CACHE (-1086) Inconsistent library cache
<<<        WAR_SWAPPING_MEDIA_IN_LIBRARY (-1087) Swapping media in library
<<<              WAR_PORT_ALREADY_IN_USE (-1088) Socket address (IP and port) already in use
<<<            WAR_SOURCE_FOR_MOVE_EMPTY (-1089) Source for volume move empty
<<<        WAR_DESTINATION_FOR_MOVE_FULL (-1090) Destination for move full
<<<                    WAR_MAILSLOT_OPEN (-1091) Mailslot in automated library is open
<<<                   WAR_MAILSLOT_EMPTY (-1092) No media in mailslot in automated library
<<<               WAR_NOT_A_FRESH_VOLUME (-1093) Not a fresh volume
<<<              WAR_FILES_ARE_DIFFERENT (-1094) Contents of the files are different
<<<                  WAR_UNREDABLE_MEDIA (-1095) Unreadable (damaged or fresh) media
<<<                  WAR_PRINTER_FAILURE (-1096) Printer related issue (extend / retract tray failed)
<<<                     WAR_SCSI_FAILURE (-1097) SCSI bus failure
<<<          WAR_FORMAT_OPERATION_FAILED (-1098) Format volume operation failed
<<<                  WAR_IO_DEVICE_ERROR (-1099) Device (hardware) IO issue
<<<          WAR_OPEN_SERIAL_PORT_FAILED (-1100) Could NOT open the serial port
<<<            WAR_DATABASE_QUERY_FAILED (-1101) Database query failed
<<<           WAR_COULD_NOT_START_THREAD (-1102) Could NOT start thread
<<<        WAR_DATABASE_OPERATION_FAILED (-1103) Database operation failed
<<<      WAR_MOVE_MEDIA_OPERATION_FAILED (-1104) Move media in automated library failed
<<<                   WAR_SOCKET_FAILURE (-1105) Socket related failure
<<<                 WAR_ANOTHER_INSTANCE (-1106) Another instance of the server is running
<<<                    WAR_TOOLKIT_ERROR (-1107) Error returned by toolkit function call
<<<               WAR_DEVICE_MALFUNCTION (-1108) Device malfunction (please check hardware)
<<<                  WAR_API_CALL_FAILED (-1109) API call failed
<<<                WAR_FILE_SYSTEM_ERROR (-1110) File system error
<<<     WAR_MISSING_ENVIRONMENT_VARIABLE (-1111) Missing environment variable
<<<             WAR_VOLUME_UNLOAD_FAILED (-1112) Could NOT unload volume from drive
<<<               WAR_VOLUME_LOAD_FAILED (-1113) Could NOT load volume into drive
<<<                      WAR_FTP_FAILURE (-1114) FTP operation failed
<<<         WAR_CLOSE_SERIAL_PORT_FAILED (-1115) Failed to close the serial port
<<<             WAR_POSITION_MOVE_FAILED (-1116) Picker position movement failure
<<<       WAR_SYSTEM_CONFIGURATION_ERROR (-1117) The software might NOT be properly configured
<<<             WAR_HARDWARE_MALFUNCTION (-1118) Hardware malfunction
<<<               WAR_SYSTEM_CALL_FAILED (-1119) Operating System (OS) call failed
<<<             WAR_INVALID_BITFILE_SIZE (-1120) Unexpected bitfile size (bitfile too small)
<<<    WAR_DATA_EXCEEDED_VOLUME_CAPACITY (-1121) Size of data file exceeded volume capacity
<<<             WAR_SOCK_BLOCK_CANCELLED (-1122) Blocking sockets call was canceled
<<<     WAR_BURN_VOLUME_OPERATION_FAILED (-1123) Burn volume operation failed
<<<               WAR_NO_DATA_TO_PROCESS (-1124) Did NOT find data to process
<<<             WAR_NO_LIBRARIES_RUNNING (-1125) No libraries are running
<<<                      WAR_FILE_EXISTS (-1126) The specified file OR folder exists
<<<                 WAR_PICKER_NOT_FOUND (-1127) Picker for the specified automated library was NOT found
<<<                WAR_COULD_NOT_GET_MAC (-1128) Could NOT get MAC from NIC
<<<              WAR_VOLUME_BEEN_ALTERED (-1129) Volume for a file has been externally altered
<<<            WAR_NO_MORE_FRESH_VOLUMES (-1130) NO more fresh volumes in automated library
<<<                        WAR_NO_DONGLE (-1131) USB license dongle NOT detected / present
<<<                     WAR_INVALID_SIZE (-1132) Invalid size
<<<                WAR_SCSI_DRIVER_ISSUE (-1133) SCSI driver interface issue
<<<           WAR_INVALID_REMOTE_ADDRESS (-1134) Remote network address is NOT valid
<<<              WAR_TRAY_MOVEMENT_ERROR (-1135) Tray movement issue
<<<                        WAR_INK_ISSUE (-1136) Ink level low OR ink cartridge malfunction
<<<              WAR_GROUPS_DO_NOT_MATCH (-1137) Group and migration group do NOT match
<<<                WAR_NOTHING_TO_BUFFER (-1138) No bitfiles to buffer
<<<              WAR_BITFILE_NOT_DELETED (-1139) Bitfile NOT deleted
<<<                     WAR_DONGLE_FOUND (-1140) USB license dongle connected
<<<                    WAR_ACCESS_DENIED (-1141) Access to the object was DENIED
<<<            WAR_RETENTION_NOT_EXPIRED (-1142) Bitfile retention has NOT expired
<<<            WAR_SIZE_B_VOLUME_MISSING (-1143) Missing side B volume ID
<<<           WAR_COULD_NOT_KILL_PROCESS (-1144) Could NOT kill a process
<<<          WAR_PROCESS_FAILED_TO_START (-1145) Process failed to start
<<<           WAR_PROCESS_FAILED_TO_STOP (-1146) Process failed to stop
<<<            WAR_INVALID_TAPE_POSITION (-1147) Invalid tape position
<<<                   WAR_DAMAGED_VOLUME (-1148) Damaged volume
<<<              WAR_BAD_FILE_DESCRIPTOR (-1149) Bad file descriptor
<<<                      WAR_SEEK_FAILED (-1150) Seek into media failed
<<<                   WAR_CORRUPT_VOLUME (-1151) Volume format unknown or corrupted
<<<            WAR_NO_NETWORK_CONNECTION (-1152) Network connection NOT detected
<<<           WAR_DATABASE_ACCESS_FAILED (-1153) Could NOT access the database server
<<<               WAR_PROCESS_IS_RUNNING (-1154) Attempted to start process but was already running
<<<      WAR_MOVE_FAILED_VOLUME_IN_DRIVE (-1155) Move operation failed leaving volume in drive
<<<                      WAR_FILE_IN_USE (-1156) File in use; could NOT open it
<<<          WAR_CENTERA_API_CALL_FAILED (-1157) Centera API call failed
<<<       WAR_SQL_SERVER_API_CALL_FAILED (-1158) SQL Server API call failed
<<<              WAR_NO_BURNER_AVAILABLE (-1159) No burner or write drive available
<<<           WAR_HARDWARE_NOT_SUPPORTED (-1160) Hardware NOT supported (please contact DatCard Systems)
<<<            WAR_MIGRATION_SERVER_FULL (-1161) Migration server is FULL
<<<                WAR_LIBRARY_NOT_READY (-1162) Automated library off-line or NOT ready
<<<     WAR_CACHE_WITH_BITFILE_NOT_FOUND (-1163) Could NOT find disk cache holding the specified bitfile
<<<                WAR_BITFILE_RELOADING (-1164) Bitfile being reloaded
<<<                 WAR_DRIVER_NOT_FOUND (-1165) Driver for drive NOT found
<<<            WAR_ENTRY_POINT_NOT_FOUND (-1166) Entry point NOT found in specified DLL
<<<            WAR_FRESH_VOLUME_IN_DRIVE (-1167) Blank, fresh or corrupt disc volume in drive
<<<        WAR_NOTHING_TO_LOCK_OR_UNLOCK (-1168) No bitfiles to lock or unlock
<<<                 WAR_BITFILE_RELOADED (-1169) The specified bitfile has been reloaded
<<<                   WAR_BITFILE_LOCKED (-1170) The specified bitfile is locked
<<<             WAR_INCORRECT_BLOCK_SIZE (-1171) Incorrect block size in media
<<<               WAR_EVENT_NOT_RECEIVED (-1172) Notification event NOT received
<<<             WAR_DEVICE_NOT_CONNECTED (-1173) The driver cannot connect to the device.
<<<             WAR_FEATURE_NOT_LICENSED (-1174) Feature NOT licensed by DatCard Systems
<<<     WAR_TOO_MANY_BITFILES_TO_PROCESS (-1175) Found too many bitfiles to process
<<<           WAR_NO_BITFILES_TO_PROCESS (-1176) No bitfile(s) found to process
<<<              WAR_SECURITY_COMPROMISE (-1177) Possible attempt to compromise security
<<<       WAR_BITFILE_TOO_BIG_FOR_VOLUME (-1178) Bitfile too large for available capacity in volume
<<<                WAR_TOO_MANY_ATTEMPTS (-1179) Too many attempts on the specified operation
<<<               WAR_MEDIA_NOT_WRITABLE (-1180) Volume in burner drive NOT writable or appendable
<<<      WAR_COULD_NOT_EXTEND_DRIVE_TRAY (-1181) Could NOT extend drive tray; media might be STUCK in drive
<<<                  WAR_SYSTEM_SHUTDOWN (-1182) Servers (services) shutting down
<<<        WAR_ERROR_WHILE_BURNING_MEDIA (-1183) Issue encountered while burning media
<<<         WAR_MEDIA_TYPE_NOT_SUPPORTED (-1184) Media type NOT supported for current operation
<<<     WAR_COULD_NOT_RETRACT_DRIVE_TRAY (-1185) Could NOT retract drive tray; media might be stuck
<<<               WAR_TOO_MANY_LOG_FILES (-1186) Too many log files for the day (message NOT logged)
<<<               WAR_NO_VOLUME_IN_DRIVE (-1187) No volume in drive OR drive tray extended
<<<   WAR_WAITING_FOR_USER_TO_MOVE_MEDIA (-1188) Waiting for user to move media in OR out of drive
<<<             WAR_STAGING_DATA_TO_BURN (-1189) Staging data to write CD / DVD volume
<<<       WAR_WRITE_TO_DRIVE_IN_PROGRESS (-1190) Writing data to CD / DVD volume in drive
<<<      WAR_READ_FROM_DRIVE_IN_PROGRESS (-1191) Reading data from CD / DVD volume in drive
<<<                 WAR_ODBC_CALL_FAILED (-1192) ODBC call failed
<<<              WAR_MERGED_BITFILE_FULL (-1193) Merged bitfile is full
<<<               WAR_GUIDS_DO_NOT_MATCH (-1194) Compared GUIDs do NOT match
<<<                WAR_INVALID_DATE_TIME (-1195) Invalid date and / or time value
<<<              WAR_GLOBAL_MUTEX_EXISTS (-1196) Global mutex found (possible duplicate process)
<<<   WAR_DECOMMISSIONED_CUSTOMER_STRING (-1197) Decommissioned customer string
<<<             WAR_NOT_ENOUGH_RESOURCES (-1198) Not enough resources to start or complete operation
<<<      WAR_INCONSISTENT_DATABASE_TABLE (-1199) Inconsistent DRIVE_TBL or VOLUME_TBL database table
<<<              WAR_NOTING_TO_DUPLICATE (-1200) No bitfiles / volumes to duplicate
<<<                        WAR_BIN_EMPTY (-1201) Source bin is empty
<<<                         WAR_BIN_FULL (-1202) Destination bin is full
<<<                   WAR_UNKNOWN_STATUS (-1203) Unknown or NOT supported status
<<<                 WAR_DID_NOT_GET_LOCK (-1204) Not able to get the lock (operation time out)
<<<               WAR_CORRUPT_TAR_HEADER (-1205) Corrupt tar header on tape leading bitfile
<<<                       WAR_DROP_ERROR (-1206) Failed to drop media (in bin or tray)
<<<             WAR_FOLDER_TAMPERED_WITH (-1207) Software distribution folder has been tampered with
<<<                  WAR_VOLUME_NOT_IDLE (-1208) Volume NOT idle (volume busy)
<<<               WAR_DEVICE_NOT_ON_LINE (-1209) Device NOT on-line
<<<                 WAR_PRINTER_OFF_LINE (-1210) Printer / Stacker off-line
<<<                    WAR_IMAGE_OVERLAP (-1211) Images overlap
<<<          WAR_UNEXPECTED_VOLUME_LABEL (-1212) Unexpected volume label
<<<              WAR_CACHE_NOT_AVAILABLE (-1213) Disk cache NOT available / accessible
<<<              WAR_TRANSFER_INCOMPLETE (-1214) Data transfered incomplete
<<<           WAR_INVALID_SOCKET_ADDRESS (-1215) Unexpected socket address (cannot bind)
<<<                   WAR_GUID_NOT_FOUND (-1216) The specifed GUID was NOT found in the CAS
<<<              WAR_WORKER_THREADS_BUSY (-1217) All worker threads in the pool are busy
<<< WAR_OPERATION_COULD_NOT_BE_PERFORMED (-1218) The requested operation could NOT be performed at this time.
<<<             WAR_DISK_CACHE_NOT_FOUND (-1219) The specified disk cache could NOT be found
<<<            WAR_UNEXPECTED_REQUEST_ID (-1220) Unexpected request ID in socket ACK
<<<               WAR_INVALID_LIBRARY_ID (-1221) Unexpected library ID
<<<                      WAR_END_OF_FILE (-1222) Attempt to read past the end of file
<<<           WAR_NETWORK_PATH_NOT_FOUND (-1223) Network path NOT accesible (remote might be busy)
<<<                 WAR_HOST_UNREACHABLE (-1224) Network cannot be reached from this host at this time
<<<                    WAR_NULL_CHECKSUM (-1225) Null (0) checksum
<<<                WAR_INVALID_FILE_NAME (-1226) Unexpected file name OR file name format
<<<                   WAR_CORRUPT_DONGLE (-1227) Data corrupted in license dongle
<<<                      WAR_SAME_DIGEST (-1228) Specified digest alreay in the CAS
<<<                     WAR_INVALID_GUID (-1229) Invalid GUID (invalid length or characters)
<<<          WAR_BITFILE_TRAILER_PRESENT (-1230) Bitfile has a bitfile trailer
<<<                  WAR_DR_FILE_MISSING (-1231) Disaster Recovery configuration file NOT found
<<<                        WAR_DOOR_OPEN (-1232) Cover OR door OR lid open in library OR stacker
<<<        WAR_NO_BARCODE_ON_TAPE_VOLUME (-1233) Tape volume missing barcode
<<<                              WAR_EOF (-1234) End Of File
<<<              WAR_NOT_FAILOVER_CONFIG (-1235) System NOT configured for failover
<<<         WAR_BECOMING_FAILOVER_MASTER (-1236) Shutting down to become failover iCAS MASTER
<<<                WAR_IP_ADDRESS_IN_USE (-1237) TCP / IP address or port already in use
<<<          WAR_BECOMING_FAILOVER_SLAVE (-1238) Shutting down to become failover iCAS SLAVE
<<< WAR_DISK_CACHE_FULL (-1239) All configured disk caches are FULL (CWM >= HWM)
<<<                 WAR_NOT_VALID_SOCKET (-1240) Descriptor NOT a valid socket (system MIGHT be OUT of resources)
<<<                     WAR_NOT_RTL_FILE (-1241) NOT an RTL file
<<<          WAR_NO_COMPRESSION_SOFTWARE (-1242) Software compression software NOT installed
<<<           WAR_START_OF_FILES_SECTION (-1243) Disaster Recovery (DR) start of FILES_SECTION
<<<               WAR_DATE_TAMPERED_WITH (-1244) System date tampered with (license dongle has been disabled)
<<<               WAR_OPERATION_TOO_SLOW (-1245) Operation is too slow (taking too much time)
<<<              WAR_INVALID_SQL_VERSION (-1246) Unexpected SQL Server version
<<<            WAR_FEATURE_NOT_SUPPORTED (-1247) Feature NOT supported on this platform
<<<            WAR_MISSING_INK_CARTRIDGE (-1248) Missing one (1) or more ink cartridge(s)
<<<    WAR_DATABASE_SERVER_NOT_AVAILABLE (-1249) Database server / engine NOT available
<<<          WAR_INVALID_CUSTOMER_STRING (-1250) Unexpected customer string
<<<                  WAR_END_OF_TAR_FILE (-1251) End of tar arhive
<<<                   WAR_PROCESS_KILLED (-1252) Process or server was killed
<<<                WAR_PROCESS_NOT_FOUND (-1253) Process or service NOT found
<<<                     WAR_SW_TOO_YOUNG (-1254) Software NOT supported by current dongle
<<<                      WAR_EMPTY_QUEUE (-1255) The specified list (queue) is EMPTY
<<<            WAR_INCOMPATIBLE_VERSIONS (-1256) Incompatible software versions detected
<<<           WAR_BURN_ATTEMPTS_EXCEEDED (-1257) Maximum number of consecutive failed burn attempts exceeded
<<<                WAR_BITFILE_NOT_FOUND (-1258) Bitfile could NOT be located
<<<      WAR_BURN_FAILED_RETURN_TO_INPUT (-1259) Disc burn operation failed
<<<                  WAR_DONGLE_DISABLED (-1260) USB license dongle is NOT enabled
<<<                  WAR_LICENSE_EXPIRED (-1261) product license has OR is about to EXPIRE
<<<                WAR_DATABASE_NOT_OPEN (-1262) Database NOT open
<<<             WAR_COLUMN_IN_DB_MISSING (-1263) Column in database table missing
<<<                  WAR_DIGEST_MISMATCH (-1264) MD5 digest mismatch
<<<                WAR_DATABASE_SHUTDOWN (-1265) Database engine shutting down
<<<           WAR_MISSING_DATABASE_TABLE (-1266) Missing database table
<<<    WAR_DATABASE_CONSTRAINT_VIOLATION (-1267) Database integrity constraint violation
<<<            WAR_NOT_ABLE_TO_CHANGE_IP (-1268) Not able to change our TCP/IP
<<<           WAR_UNEXPECTED_TCP_IP_MASK (-1269) Unexpected TCP/IP mask
<<<            WAR_COULD_NOT_MOUNT_DRIVE (-1270) Could NOT mount remote drive
<<<               WAR_HSM_DRIVER_FAILURE (-1271) HSMDriver filter failure
<<<                   WAR_BITFILE_ROUTED (-1272) Bitfile has already been routed
<<<                   WAR_MISSING_STREAM (-1273) Specified stream missing from file
<<<              WAR_MISSING_DATA_SOURCE (-1274) ODBC system data source NOT found
<<<               WAR_ICAS_NOT_AVAILABLE (-1275) iCAS NOT available
<<<                     WAR_DONGLE_ISSUE (-1276) Issue while accessing the USB license dongle
<<<               WAR_NEGATIVE_TIME_DIFF (-1277) Negative time difference (daylight savings fallback)
<<<             WAR_NETWORK_PATH_DELETED (-1278) Network path deleted
<<<           WAR_COULD_NOT_UNMOUT_DRIVE (-1279) Could NOT unmount remote drive
<<<              WAR_COULD_NOT_OPEN_FILE (-1280) Could NOT open file
<<<         WAR_COULD_NOT_WRITE_METADATA (-1281) Could NOT write metadata
<<<          WAR_COULD_NOT_READ_METADATA (-1282) Could NOT read metadata
<<<              WAR_SEMAPHORE_TIMED_OUT (-1283) Semaphore timeout period has expired
<<<              WAR_NETWORK_UNREACHABLE (-1284) Network location CANNOT be reached
<<<                     WAR_STACKER_BUSY (-1285) Automated stacker BUSY
<<<              WAR_CACHE_NOT_SPECIFIED (-1286) Cache path NOT specified
<<<          WAR_STUDY_FOLDERS_NOT_FOUND (-1287) Folder(s) with studies NOT found
<<<                WAR_ENCRYPTION_FAILED (-1288) Encrypted bitfile transfer failed
<<<            WAR_DONGLE_NOT_PROGRAMMED (-1289) USB license dongle has NOT been programmed
<<<               WAR_DEVICE_NOT_WORKING (-1290) Attached device is NOT functioning
<<<           WAR_DRIVE_LETTER_NOT_FOUND (-1291) File system with specified drive letter NOT found
<<<                  WAR_GROUP_NOT_FOUND (-1292) Specified Group ID NOT found
<<<           WAR_SOCKET_SYSTEM_NOT_INIT (-1293) Socket mechanism NOT initialized yet
<<<             WAR_DONGLE_NOT_CONNECTED (-1294) USB license dongle NOT connected
<<<           WAR_CORRUPT_DATA_STRUCTURE (-1295) Data structure appears to be corrupt
<<<                  WAR_WORK_NOT_POSTED (-1296) Work NOT posted to pool of worker threads
<<<               WAR_TOO_MANY_Q_ENTRIES (-1297) Too many entries in the queue
<<<                WAR_INVALID_ARGUMENTS (-1298) Invalid arguments or argument buffer too small
<<<       WAR_INVALID_NETWORK_CONNECTION (-1299) Operation attempted on nonexistent connection
<<<        WAR_SYSTEM_CALL_NOT_AVAILABLE (-1300) Operating System call NOT available
<<<               WAR_DELETIONS_DISABLED (-1301) Bitfile deletions are DISABLED
<<<                   WAR_INVALID_OPTION (-1302) INVALID option was specified
<<<          WAR_OPERATION_NOT_SUPPORTED (-1303) Operation NOT supported
<<<                WAR_FUNCTION_OBSOLETE (-1304) Function / Method has been OBSOLETED
<<<             WAR_SOCKET_ALREADY_BOUND (-1305) Socket already bound to an address
<<<             WAR_SOCKET_NOT_CONNECTED (-1306) Socket NOT connected
<<<                WAR_LISTEN_NOT_CALLED (-1307) Listen function not invoked prior to accept
<<<            WAR_GUID_RECORD_NOT_FOUND (-1308) Record for specified GUID not in the database
<<<                WAR_SEPARATE_METADATA (-1309) Bitfiles have separate metadata
<<<       WAR_USER_ID_NAME_NOT_SPECIFIED (-1310) User ID or name NOT specified
<<<               WAR_USER_NOT_LOGGED_ON (-1311) User NOT logged on
<<<          WAR_INVALID_MERGE_SIGNATURE (-1312) Incorrect merged bitfile signature
<<<     WAR_INCORRECT_NUMBER_OF_BITFILES (-1313) Incorrect number of bitfiles in merged bitfile
<<<              WAR_INVALID_SESSION_KEY (-1314) Invalid or non-matching session key
<<<                  WAR_INVALID_USER_ID (-1315) Invalid or non-matching user ID
<<<                WAR_INVALID_USER_NAME (-1316) Invalid or non-matching user name
<<<                WAR_INVALID_ARG_COUNT (-1317) Invalid count of arguments
<<<              WAR_FUNCTION_DEPRECATED (-1318) Function / method / procedure has been deprecated
<<<                   WAR_INVALID_OFFSET (-1319) Invalid offset
<<<                    WAR_INVALID_COUNT (-1320) Invalid count
<<<    WAR_DIGEST_AND_GUID_NOT_SPECIFIED (-1321) Digest and GUID NOT specified
<<<               WAR_TYPES_DO_NOT_MATCH (-1322) Value types do NOT match
<<<           WAR_FOUND_BITFILE_IN_CACHE (-1323) Bitfile found in a disk cache
<<<                  WAR_SOCKET_SHUTDOWN (-1324) Socket has been shutdown
<<<                     WAR_INVALID_PORT (-1325) Unexpected TCP / IP port
<<<                     WAR_MSMQ_FAILURE (-1326) MSMQ call failed
<<<                  WAR_QUEUE_NOT_FOUND (-1327) Queue NOT found
<<<           WAR_ODBC_SUCCESS_WITH_INFO (-1328) ODBC call returned success with information
<<<               WAR_CORRUPT_DISK_CACHE (-1329) The specified disk cache is corrupt
<<<                    WAR_PATH_TOO_LONG (-1330) The specified path is too long
<<<                 WAR_LISTENING_SOCKET (-1331) The socket is a listening socket
<<<                     WAR_INVALID_FLAG (-1332) Unexpected bitfile flag
<<<                  WAR_MUTEX_IS_LOCKED (-1333) The mutex has been LOCKED for a while
<<<                 WAR_BITFILE_IN_STORE (-1334) Bitfile already in the storage server
<<<                WAR_INVALID_CHARACTER (-1335) Invalid character in buffer
<<<               WAR_DBMS_NOT_SUPPORTED (-1336) DBMS version NOT supported
<<<                 WAR_TOO_MANY_SOCKETS (-1337) Too many open sockets
<<<                   WAR_INVALID_TCP_IP (-1338) Unexpected TCP / IP address
<<<                WAR_BITFILE_ENCRYPTED (-1339) Attempt to encrypt an encrypted bitfile
<<<              WAR_BITFILE_UNENCRYPTED (-1340) Attempt to unencrypt an unencrypted bitfile
<<<                   WAR_MERGED_BITFILE (-1341) This is a MERGED bitfile
<<<        WAR_BITFILE_TRAILER_TOO_SMALL (-1342) Bitfile trailer (damaged) too small
<<<                WAR_INVALID_PARAMETER (-1343) Invalid parameter
<<<           WAR_BUFFER_COMPARE_FAILURE (-1344) Write-Read-Compare failed with buffer
<<<             WAR_FILE_COMPARE_FAILURE (-1345) Write-Read-Compare failed with file
<<<        WAR_DSN_ARCHITECTURE_MISMATCH (-1346) DSN architecture mismatch between Driver and Application
<<<           WAR_HARD_LICENSE_NO_DONGLE (-1347) Found hard license WITHOUT USB license dongle present
<<<         WAR_SOFT_LICENSE_WITH_DONGLE (-1348) Found soft license WITH USB license dongle present
<<<                             WAR_XOFF (-1349) Too many PENDING requests in the iCAS
<<<                       WAR_TABLE_FULL (-1350) Hash table full
<<<            WAR_ADDRESS_NOT_AVAILABLE (-1351) Address not available (might be in use)
<<<     WAR_HASH_TABLE_ALREADY_ALLOCATED (-1352) Hash table has already been allocated
<<<      WAR_TOO_MANY_THREADS_TO_WAIT_ON (-1353) Too many threads to wait on
<<<         WAR_SOCK_INIT_ALREADY_CALLED (-1354) Method SockInit() has already been called
<<<      WAR_SOCK_CLEANUP_ALREADY_CALLED (-1355) Method SockCleanUp has already been called
<<<                      WAR_OPEN_SOCKET (-1356) Open socket; should be closed
<<<                   WAR_TOO_MANY_GUIDS (-1357) Too many GUIDs have been specified
<<<                     WAR_BUFFER_EMPTY (-1358) Buffer is EMPTY
<<<                      WAR_BUFFER_FULL (-1359) Buffer is FULL
<<<           WAR_OBJECT_NOT_INITIALIZED (-1360) Object has NOT been initialized

As you can tell the warning codes have names that attempts to describe the issue at hand. The codes are associated with a set of strings that provide a better description of the error code.

Now let’s look at exceptions. In software, exception handling is a mechanism used to respond to unexpected conditions when executing code. For example, if a sequence of instruction in a program performs a division by zero, the programmer may or may not be aware of the possible condition and may or may not have written some code to detect, inform and more important recover from the situation. To read more about Exception Handling you may wish to read the associated Wikipedia entry.

There are advantages and disadvantages when using exceptions. On the positive side, if the code encounters an exception (e.g., division by zero, out of memory) an exception would automatically be raised and if not handled, the software will be terminated. Personally I do not like to see software that I have developed crash because a condition was not handled. When the possibility of an error is encountered, the software developer should address it and attempt to address it, or gracefully terminate the method / function freeing the required resources and return to the caller.

If you have the code several levels deep, and an error is encountered, the best possible way to handle it is to return to the next function / method in the calling stack and determine if the issue may be addressed there. Eventually the issue may reach the main / entry point in the program. The decision could then be made to proceed or to terminate the program. For example, if one wrote a function that encrypts a file and the specified file is not found, not much can be done to recover. A descriptive error should be displayed and the program should exit. On the other hand if you are running a storage server, it is not reasonable if a file is not found, the storage server, which could be at the time of the issue may be processing thousands of requests, crash or exit.

As we previously mentioned, a returned value is typically an integer returned by a function. If all is well traditionally the returned value is zero. Any non-zero value indicates an error or a warning. The drawback of this mechanism is that if the cause for the error is not programmed, the error may crash the program.  In addition, there are functions / methods that will automatically generate an exception. In such cases, if the exception is thrown by a method / function called, the only thing that can be done is to catch the exception and proceed as needed.

In general (never say always) the cost of throwing an exception is quite larger when comparing to returning a value when a method / function is called. This provides a mixed environment with returned values and exception handling.

I always like to make an analogy to exception throwing and garbage collection. In some programming languages (e.g., C), the programmer is responsible to free all resources that will not be returned to the caller. If they are not released, then on each invocation of the function / method some memory will be leaked. Sooner or later the program will crash while executing a completely different and possibly unrelated function. Such issue may be hard to find. Most OO languages have a mechanism to turn off garbage collection and let the developers manage their own resources.

So we are faced with the dilemma of what is better, to throw / handle exceptions or return values?

When properly used with log files, issues in the code can be easily diagnose and addressed. That said; depending on the programming language, some functions / methods may throw exceptions and the only thing the developer can do is to catch them. At that time it is always up to the developer to attempt to resolve the issue and if not possible, pass or throw a new exception and let the caller make the decision to continue or exit.

In general functions / methods should always be enclosed in a try-catch-finally block. One way or the other the software developers must be clear on the set of steps that are needed to check what went on a function / method. Any try-catch-finally block can always be made to return an integer value. The development team should decide on a coding standard and then adhere to it. Do not forget to always conduct code reviews. There are many tools that support such effort.

If you do not like to return an integer value; then make it so it is quite difficult for a function / method throw an exception. Exceptions should be relegated as the ultimate resource if nothing else can be done to address an unexpected condition.

The following code in C# implements a CLI for a storage server that may be used to determine if the server is available:

// **** ****
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

// **** ****
using System.Runtime.InteropServices;
using System.Threading;

// **** ****
using CSharpiCASAPI;

namespace icasping
{

	public class icasping
	{

		static int Main	(
						string[]	args
						)

		//	****************************************************************************@@
		//  - This Command Line Interface (CLI) is used to send the storage server a
		//  message to check if the server is up and running.
		//	******************************************************************************
			
		{

		bool		verbose		= false;

		int			retVal		= 0,
					status		= 0;

		string		serverIP	= "0.0.0.0";

		ushort		serverPort	= 4444;

		// **** parse arguments ****
		for (int i = 0; i < args.Length; i++) { // **** get next argument **** string argument = args[i]; // **** check if this is a flag argument **** if (argument.StartsWith("-")) { // **** check if the storage server IP has been specified **** if (argument.StartsWith("-s")) { // **** determine if the actual IP has been omitted **** if ((i + 1) >= args.Length)
						{
						Console.WriteLine(	"icasping <<< UNEXPECTED args.Length: {0} i: {1}", args.Length, i);
						retVal = -1;
						goto done;
						}

					// **** extract the storage server IP (if possible) ****
					serverIP = string.Copy(args[++i]);

					// **** inform the user what is going on ****
					if (verbose)
						Console.WriteLine("icasping <<< serverIP ==>{0}<==", serverIP); } // **** server port number has been specified **** else if (argument.StartsWith("-p")) { // **** check if the actual port number has not been specified **** if ((i + 1) >= args.Length)
						{
						Console.WriteLine("icasping <<< UNEXPECTED args.Length: {0} i: {1}", args.Length, i);
						retVal = -1;
						goto done;
						}

					// **** extract the server port ****
					UInt16.TryParse(args[++i],
									out serverPort);

					// **** inform the user what is going on ****
					if (verbose)
						Console.WriteLine("icasping <<< serverPort: {0}", serverPort);
					}

				// **** turn on verbose mode ****
				else if (argument.StartsWith("-v"))
					{

					// **** turn on the verbose mode ****
					verbose = true;

					// **** inform the user what is going on ****
					if (verbose)
						Console.WriteLine("icasping <<< verbose: {0}", verbose);
					}

				// **** unknown argument ****
				else
					{
					Console.WriteLine("icasping <<< UNEXPECTED args.Length: {0} i: {1}", args.Length, i);
					}

				// **** skip this argument ****
				continue;
				}
			}

		// **** invoke the unmanaged code function (may throw exception) ****
		try
			{
			status = CSharpiCASAPI.CSharpiCASAPI.ICASPing(	serverIP,
															serverPort);
			}
		catch (Exception ex)
			{
			Console.WriteLine("icasping <<< EXCEPTION CSharpiCASAPI.CSharpiCASAPI.ICASPing ex.Message ==>{0}<== status: {1}", ex.Message, status);
			Int32.TryParse(	ex.Message,
							out status);
			retVal = status;
			Console.WriteLine("icasping <<< retVal: {0}", retVal);
			goto done;
			}
		finally
			{}

		// **** check if something went wrong ****
		if (status != 0)
			{
			Console.WriteLine("icasping <<< CSharpiCASAPI.CSharpiCASAPI.ICASPing status: {0}", status);
			retVal = status;
			goto done;
			}

		// **** inform the user what is going on ****
		Console.WriteLine("icasping <<< iCAS reply from serverIP ==>{0}<==", serverIP);

		// **** clean up ****
		done:

		// **** delay execution to allow user to read the error message ****
		if (retVal != 0)
			Thread.Sleep(1000);

		// **** inform the caller what went on ****
		return retVal;
		}
	}
}

The declaration of the iCASPing() entry point follows:

&lt;&lt; iCASPing() declaration in cSharpiCASAPI.cs&gt;&gt;

[DllImport("sdmapi.dll", EntryPoint = "iCASPing")]
	private static extern int iCASPing	(
										[MarshalAs(UnmanagedType.LPStr)]	string			serverIP,
										[MarshalAs(UnmanagedType.U2)]		ushort			serverPort									);

Code for the iCASPing() function follows:

__declspec(dllexport) int __stdcall	iCASPing	(
												char			serverIP[BUFSIZ / 4],
												unsigned short	serverPort
												)

//	*************************************************************@@
//	- This function may used by clients to check if a specified
//	instance of a storage server is ready to process requests.
//
//	This function / method is called by the C# interface to the
//	storage server.
//	***************************************************************

{

BOOL				sockInit;							// flag that socket mechanism has been initialized

char				myServerIP[BUFSIZ / 4];				// my server IP

int					retVal,								// value returned by this function
					status;								// returned by function calls

SENCOR_CONNECTION	connection;							// connection data structure

// **** initialization ****
retVal		= WAR_SUCCESS;								// hope all goes well

sockInit	= (BOOL)(0 == 1);							// for starters

memset((void *)&connection,	(int)0x00, (size_t)sizeof(connection));
memset((void *)myServerIP,	(int)0x00, (size_t)sizeof(myServerIP));

// **** log what is going on ****
if (traceExecution != 0)
	EventLog(EVENT_INFO,
	"iCASPing <<< ENTERING line: %d\n",
	__LINE__);

// **** perform sanity checks ****
if (serverIP == (char *)NULL)
	{
	EventLog(EVENT_ERROR,
	"iCASPing <<< UNEXPECTED serverIP == NULL line: %d file ==>%s<==\n", __LINE__, __FILE__); retVal = WAR_INVALID_ARGUMENT; // flag the issue goto done; // perform cleanup } // **** log what is going on **** if (traceExecution > 0)
	EventLog(EVENT_INFO,
	"iCASPing <<< serverIP ==>%s<== line: %d\n",
	serverIP, __LINE__);

// **** ****
if (serverPort == (unsigned short)0)
	{
	EventLog(EVENT_ERROR,
	"iCASPing <<< UNEXPECTED serverPort: %hu line: %d file ==>%s<==\n", serverPort, __LINE__, __FILE__); retVal = WAR_INVALID_ARGUMENT; // flag the issue goto done; // perform cleanup } // **** log what is going on **** if (traceExecution > 0)
	EventLog(EVENT_INFO,
	"iCASPing <<< serverPort: %hu line: %d\n",
	serverPort, __LINE__);

// **** initialize the socket mechanism (mandatory first call) ****
status = SockInit();
switch (status)
	{
	case 0:
	
		// **** all is well so far ****
		
	break;
	
	default:
		PrintErrorCode(status, __LINE__, __FILE__);
		EventLog(EVENT_ERROR,
		"iCASPing <<< SockInit status: %d line: %d file ==>%s<==\n",
		status, __LINE__, __FILE__);
		retVal = status;								// flag the issue
		goto done;										// perform cleanup
	break;
	}

// **** flag that the socket mechanism has been initialized ****
sockInit = (BOOL)(1 == 1);

// **** get the local server IP (if needed) ****
if ((*serverIP                   == '\0') ||
	(strcmp(serverIP, "0.0.0.0") == 0))
	{

	// **** use the hint from the registry key ****
	strcpy(myServerIP, sdmServerIP);

	// **** get our IP ****
	status = SockGetMyIP(myServerIP);
	CDP_CHECK_STATUS("iCASPing <<< SockGetMyIP", status); } else { strcpy(myServerIP, serverIP); } // **** log what is going on **** if (traceExecution > 0)
	EventLog(EVENT_INFO,
	"iCASPing <<< myServerIP ==>%s<== line: %d\n",
	myServerIP, __LINE__);

// **** initialize the connection data structure ****
status = CASConnInit(	&connection,
						myServerIP,
						serverPort);
switch (status)
	{
	case 0:
	
		// **** all is well so far ****
		
	break;
	
	default:
		PrintErrorCode(status, __LINE__, __FILE__);
		EventLog(EVENT_ERROR,
		"iCASPing <<< CASConnInit status: %d myServerIP ==>%s<== serverPort: %hu line: %d file ==>%s<==\n",
		status, myServerIP, serverPort, __LINE__, __FILE__);
		retVal = status;								// flag the issue
		goto done;										// perform cleanup
	break;
	}

// **** ping the specified storage server ****
status = CASPing(&connection);
switch (status)
	{
	case 0:
	
		// **** all is well so far ****
		
	break;

	case WAR_CONNECTION_REFUSED:
		EventLog(EVENT_ERROR,
		"iCASPing <<< CASPing WAR_CONNECTION_REFUSED myServerIP ==>%s<== serverPort: %hu line: %d file ==>%s<==\n",
		myServerIP, serverPort, __LINE__, __FILE__);
		retVal = status;
		goto done;
	break;

	case WAR_CONNECTION_CLOSED:
		EventLog(EVENT_ERROR,
		"iCASPing <<< CASPing WAR_CONNECTION_CLOSED myServerIP ==>%s<== serverPort: %hu line: %d file ==>%s<==\n",
		myServerIP, serverPort, __LINE__, __FILE__);
		retVal = status;
		goto done;
	break;

	case WAR_SOCK_TIMEOUT:
		EventLog(EVENT_ERROR,
		"iCASPing <<< CASPing WAR_SOCK_TIMEOUT myServerIP ==>%s<== serverPort: %hu line: %d file ==>%s<==\n",
		myServerIP, serverPort, __LINE__, __FILE__);
		retVal = status;
		goto done;
	break;

	case WAR_SOCKET_FAILURE:
		EventLog(EVENT_ERROR,
		"iCASPing <<< CASPing WAR_SOCKET_FAILURE myServerIP ==>%s<== serverPort: %hu line: %d file ==>%s<==\n",
		myServerIP, serverPort, __LINE__, __FILE__);
		retVal = status;
		goto done;
	break;

	case WAR_CONNECTION_RESET:
		EventLog(EVENT_ERROR,
		"iCASPing <<< CASPing WAR_CONNECTION_RESET myServerIP ==>%s<== serverPort: %hu line: %d file ==>%s<==\n",
		myServerIP, serverPort, __LINE__, __FILE__);
		retVal = status;
		goto done;
	break;

	case WAR_UNEXPECTED_REQUEST_ID:
		EventLog(EVENT_ERROR,
		"iCASPing <<< CASPing WAR_UNEXPECTED_REQUEST_ID myServerIP ==>%s<== serverPort: %hu line: %d file ==>%s<==\n",
		myServerIP, serverPort, __LINE__, __FILE__);
		retVal = status;
		goto done;
	break;

	case WAR_INVALID_REMOTE_ADDRESS:
		EventLog(EVENT_ERROR,
		"iCASPing <<< CASPing WAR_INVALID_REMOTE_ADDRESS myServerIP ==>%s<== serverPort: %hu line: %d file ==>%s<==\n",
		myServerIP, serverPort, __LINE__, __FILE__);
		retVal = status;
		goto done;
	break;

	case WAR_INVALID_ARGUMENT:
		EventLog(EVENT_ERROR,
		"iCASPing <<< CASPing WAR_INVALID_ARGUMENT myServerIP ==>%s<== serverPort: %hu line: %d file ==>%s<==\n",
		myServerIP, serverPort, __LINE__, __FILE__);
		retVal = status;
		goto done;
	break;
	
	default:
		PrintErrorCode(status, __LINE__, __FILE__);
		EventLog(EVENT_ERROR,
		"iCASPing <<< CASPing status: %d myServerIP ==>%s<== serverPort: %hu line: %d file ==>%s<==\n",
		status, myServerIP, serverPort, __LINE__, __FILE__);
		retVal = status;
		goto done;
	break;
	}

// **** clean up ****
done:

// **** clear the connection data structure ****
status = ConnClear(&connection);
if (status != 0)
	EventLog(EVENT_ERROR,
	"iCASPing <<< ConnClear status: %d line: %d file ==>%s<==\n",
	status, __LINE__, __FILE__);

// **** clean up the socket mechanism (if needed) ****
if (sockInit)
	{
	status = SockCleanUp();
	if (status != 0)									// something went wrong
		EventLog(EVENT_ERROR,
		"iCASPing <<< SockCleanUp status: %d line: %d file ==>%s<==\n",
		status, __LINE__, __FILE__);
	}

// **** log what is going on ****
if ((traceExecution != 0) || (retVal != 0))
	EventLog(EVENT_INFO,
	"iCASPing <<< retVal: %d line: %d\n",
	retVal, __LINE__);

// **** inform caller what went on ****
return retVal;
}

And finally the code for the storage server API CASPing() in C follows:

_declspec(dllexport) int __stdcall	CASPing	(
											SENCOR_CONNECTION	*connection
											)

//	***************************************************************@@
//	- This API call just causes the server to send back a reply.
//	This call is used to verify that the server is listening for 
//	requests from clients.
//	*****************************************************************

{

int			retVal,										// returned by this function
			status;										// returned by function calls

SOCKET		srSocket;									// request socket

SOCKET_ACK	*socketAck;									// socket acknowledgemnet

SOCKET_CMD	*socketCmd;									// socket command

// **** initialization ****
retVal		= WAR_SUCCESS;								// hope all goes well

srSocket	= INVALID_SOCKET;							// for starters
socketAck	= (SOCKET_ACK *)NULL;						// for starters
socketCmd	= (SOCKET_CMD *)NULL;						// for starters
status		= 0;										// for starters

// **** inform the user what went on ****
if (traceExecution != 0)
	EventLog(EVENT_INFO,
	"CASPing <<< ENTERING line: %d\n",
	__LINE__);

// **** perform sanity checks ****
if (connection == (SENCOR_CONNECTION *)NULL)			// invalid pointer
	{
	EventLog(EVENT_ERROR,
	"CASPing <<< UNEXPECTED connection == NULL line: %d file ==>%s<==\n",
	__LINE__, __FILE__);
	retVal = WAR_INVALID_ARGUMENT;						// flag the issue
	goto done;											// to perform cleanup
	}

// **** allocate data structures ****
socketAck = (SOCKET_ACK *)calloc(	(size_t)1,
									(size_t)sizeof(SOCKET_ACK));
if (socketAck == (SOCKET_ACK *)NULL)					// allocation failed
	{
	EventLog(EVENT_ERROR,
	"CASPing <<< calloc SOCKET_ACK line: %d file ==>%s<==\n",
	__LINE__, __FILE__);
	retVal = WAR_NO_MORE_MEMORY;						// flag the issue
	goto done;											// perform cleanup
	}

socketCmd = (SOCKET_CMD *)calloc(	(size_t)1,
									(size_t)sizeof(SOCKET_CMD));
if (socketCmd == (SOCKET_CMD *)NULL)					// allocation failed
	{
	EventLog(EVENT_ERROR,
	"CASPing <<< calloc SOCKET_CMD line: %d file ==>%s<==\n",
	__LINE__, __FILE__);
	retVal = WAR_NO_MORE_MEMORY;						// flag the issue
	goto done;											// perform cleanup
	}

// **** set the connection authentication fields (if needed) ****
if (checkUserCredentials)
	{
	
	// **** set connection authentication fields ****
	status = ConnSetAuthentication(connection);
	CDP_CHECK_STATUS("CASPing <<< ConnSetAuthentication", status);
	
	// **** copy the authentication fields to the command ****
	status = AutheticateCommand(connection,
								socketCmd);
	CDP_CHECK_STATUS("CASPing <<< AutheticateCommand", status); } // **** set the command / request for the storage server **** socketCmd->requestType = SOCKET_CMD_IS_ALIVE;

// **** log what is going on ****
if (traceExecution > 1)
	{
	status = ConnDump(connection);
	status = SockCmdDump(socketCmd);
	}

// **** log what is going on ****
if (traceExecution > 0)
	EventLog(EVENT_INFO,
	"CASPing <<< BEFORE SockSendCommand serverIP ==>%s<== serverPort: %hu line: %d ...\n", connection->serverIP, connection->serverPort, __LINE__);

// **** send the request to the storage server ****
status = SockSendCommand(	connection->serverIP,
							socketCmd,
							socketAck,
							connection->serverPort,
				
							connection->clientPort,
							CAS_PING_SOCKET_TIMEOUT,
							CAS_PING_SOCKET_TIMEOUT,
							&srSocket);

// **** log what is going on ****
if (traceExecution > 0)
	EventLog(EVENT_INFO,
	"CASPing <<<  AFTER SockSendCommand srSocket: 0x%08lx line: %d\n",
	(unsigned long)srSocket, __LINE__);

// **** proceed based on the returned status ****
switch (status)
	{
	case 0:
	
		// **** all is well so far ****
		
	break;

	case WAR_CONNECTION_REFUSED:
		EventLog(EVENT_ERROR,
		"CASPing <<< SockSendCommand WAR_CONNECTION_REFUSED line: %d file ==>%s<==\n",
		__LINE__, __FILE__);
		retVal = status;
		goto done;
	break;

	case WAR_CONNECTION_CLOSED:
		EventLog(EVENT_ERROR,
		"CASPing <<< SockSendCommand WAR_CONNECTION_CLOSED line: %d file ==>%s<==\n",
		__LINE__, __FILE__);
		retVal = status;
		goto done;
	break;

	case WAR_SOCK_TIMEOUT:
		EventLog(EVENT_ERROR,
		"CASPing <<< SockSendCommand WAR_SOCK_TIMEOUT line: %d file ==>%s<==\n",
		__LINE__, __FILE__);
		retVal = status;
		goto done;
	break;

	case WAR_SOCKET_FAILURE:
		EventLog(EVENT_ERROR,
		"CASPing <<< SockSendCommand WAR_SOCKET_FAILURE line: %d file ==>%s<==\n",
		__LINE__, __FILE__);
		retVal = status;
		goto done;
	break;

	case WAR_CONNECTION_RESET:
		EventLog(EVENT_ERROR,
		"CASPing <<< SockSendCommand WAR_CONNECTION_RESET line: %d file ==>%s<==\n",
		__LINE__, __FILE__);
		retVal = status;
		goto done;
	break;

	case WAR_UNEXPECTED_REQUEST_ID:
		EventLog(EVENT_ERROR,
		"CASPing <<< SockSendCommand WAR_UNEXPECTED_REQUEST_ID line: %d file ==>%s<==\n",
		__LINE__, __FILE__);
		retVal = status;
		goto done;
	break;

	case WAR_INVALID_REMOTE_ADDRESS:
		EventLog(EVENT_ERROR,
		"CASPing <<< SockSendCommand WAR_INVALID_REMOTE_ADDRESS line: %d file ==>%s<==\n",
		__LINE__, __FILE__);
		retVal = status;
		goto done;
	break;

	case WAR_INVALID_ARGUMENT:
		EventLog(EVENT_ERROR,
		"CASPing <<< SockSendCommand WAR_INVALID_ARGUMENT connection->serverIP ==>%s<== line: %d file ==>%s<==\n", connection->serverIP, __LINE__, __FILE__);
		retVal = status;
		goto done;
	break;

	case WAR_HOST_UNREACHABLE:
		EventLog(EVENT_ERROR,
		"CASPing <<< SockSendCommand WAR_HOST_UNREACHABLE connection->serverIP ==>%s<== line: %d file ==>%s<==\n", connection->serverIP, __LINE__, __FILE__);
		retVal = status;
		goto done;
	break;

	case WAR_SOCKET_SHUTDOWN:
		EventLog(EVENT_ERROR,
		"CASPing <<< SockSendCommand WAR_SOCKET_SHUTDOWN line: %d file ==>%s<==\n",
		__LINE__, __FILE__);
		retVal = status;
		goto done;
	break;

	case WAR_PORT_ALREADY_IN_USE:
		EventLog(EVENT_ERROR,
		"CASPing <<< SockSendCommand WAR_PORT_ALREADY_IN_USE line: %d file ==>%s<==\n",
		__LINE__, __FILE__);
		retVal = status;
		goto done;
	break;

	case WAR_CONNECTION_ABORTED:
		EventLog(EVENT_ERROR,
		"CASPing <<< SockSendCommand WAR_CONNECTION_ABORTED line: %d file ==>%s<==\n",
		__LINE__, __FILE__);
		retVal = status;
		goto done;
	break;

	case WAR_NETWORK_UNREACHABLE:
		EventLog(EVENT_ERROR,
		"CASPing <<< SockSendCommand WAR_NETWORK_UNREACHABLE line: %d file ==>%s<==\n",
		__LINE__, __FILE__);
		retVal = status;
		goto done;
	break;

	case WAR_FILE_EXISTS:
		EventLog(EVENT_ERROR,
		"CASPing <<< SockSendCommand WAR_FILE_EXISTS line: %d file ==>%s<==\n",
		__LINE__, __FILE__);
		retVal = status;
		goto done;
	break;

	default:
		PrintErrorCode(status, __LINE__, __FILE__);
		EventLog(EVENT_ERROR,
		"CASPing <<< SockSendCommand status: %d srSocket: 0x%08lx line: %d file ==>%s<==\n", status, (unsigned long)srSocket, __LINE__, __FILE__); retVal = status; // flag the issue goto done; // perform cleanup break; } // **** check the status in the ACK packet **** switch (socketAck->status)
	{
	case 0:
	
		// **** all is well so far ****
		
	break;
	
	case WAR_USER_ID_NAME_NOT_SPECIFIED:
		EventLog(EVENT_ERROR,
		"CASPing <<< UNEXPECTED socketAck->status: WAR_USER_ID_NAME_NOT_SPECIFIED line: %d file ==>%s<==\n", __LINE__, __FILE__); retVal = (int)socketAck->status;
		goto done;										// perform cleanup
	break;
	
	case WAR_USER_NOT_LOGGED_ON:
		EventLog(EVENT_ERROR,
		"CASPing <<< UNEXPECTED socketAck->status: WAR_USER_NOT_LOGGED_ON line: %d file ==>%s<==\n", __LINE__, __FILE__); retVal = (int)socketAck->status;
		goto done;										// perform cleanup
	break;
	
	default:
		PrintErrorCode((int)socketAck->status, __LINE__, __FILE__);
		EventLog(EVENT_ERROR,
		"CASPing <<< UNEXPECTED socketAck->status: %ld line: %d file ==>%s<==\n", socketAck->status, __LINE__, __FILE__);
		retVal = (int)socketAck->status;
		goto done;										// perform cleanup
	break;
	}

// **** log what is going on ****
if (traceExecution > 1)
	{
	status = SockCmdDump(socketCmd);
	status = SockAckDump(socketAck);
	}

// **** check that the request socket is not open ****
if ((srSocket == INVALID_SOCKET) ||
    (srSocket == (SOCKET)0))
	{}
else
	{
	EventLog(EVENT_ERROR,
	"CASPing <<< UNEXPECTED srSocket: 0x%08lx line: %d file ==>%s<==\n", (unsigned long)srSocket, __LINE__, __FILE__); retVal = WAR_OPEN_SOCKET; goto done; } // **** clean up**** done: // **** close the request socket (if needed) **** if ((srSocket == INVALID_SOCKET) || (srSocket == (SOCKET)0)) {} else { // **** log what is going on **** if (traceExecution > 0)
		EventLog(EVENT_INFO,
		"CASPing <<< BEFORE SockClose srSocket: 0x%08lx line: %d ...\n", (unsigned long)srSocket, __LINE__); // **** close the request socket **** status = SockClose(srSocket); // **** log what is going on **** if (traceExecution > 0)
		EventLog(EVENT_INFO,
		"CASPing <<<  AFTER SockClose srSocket: 0x%08lx line: %d\n",
		(unsigned long)srSocket, __LINE__);

	// **** proceed based on the returned status ****
	switch (status)
		{
		case 0:

			// **** all is well so far ****

		break;

		case WAR_INVALID_ARGUMENT:
			EventLog(EVENT_ERROR,
			"CASPing <<< SockClose WAR_INVALID_ARGUMENT srSocket: 0x%08lx line: %d file ==>%s<==\n",
			(unsigned long)srSocket, __LINE__, __FILE__);
		break;

		default:
			PrintErrorCode(status, __LINE__, __FILE__);
			EventLog(EVENT_ERROR,
			"CASPing <<< SockClose status: %d srSocket: 0x%08lx line: %d file ==>%s<==\n",
			status, (unsigned long)srSocket, __LINE__, __FILE__);
		break;
		}

	// **** flag that the socket has been closed ****
	srSocket = INVALID_SOCKET;
	}

// **** free data structures (if needed) ****
if (socketAck != (SOCKET_ACK *)NULL)
	free((void *)socketAck);

if (socketCmd != (SOCKET_CMD *)NULL)
	free((void *)socketCmd);

// **** log what is going on ****
if (traceExecution != 0)
	EventLog(EVENT_INFO,
	"CASPing <<< retVal: %d line: %d\n",
	retVal, __LINE__);

// **** inform the caller what went on ****
return retVal;
}

As you can see the C code uses returned codes and the C# uses exceptions and / or returned values. What is important is to be consistent depending on the programming language used to make calls. If developing using the C programming language use returned values, but if using C# use exceptions. The sample code illustrates how easy it is to use both methods.

If you have comments or questions regarding this or any other post in this blog, please leave me a message down below, or if you prefer send me a message via email. I try to spend a couple hours every day learning, experimenting and posting in this blog.

John

john.canessa@gmail.com

@john_canessa

2 thoughts on “Returned Value versus Exception Handling”

  1. Dear John,

    very good article. But in my opinion I would not question if I should use integers or exceptions. For me, returning integers is just a workaround when the language does not provide me with concept for an exception.
    An exception is crystal clear in its intention. It is not some value that has be interpreted by a developer first. It indicates a problem and if you don’t deal with it, your application crashes.

    Greetings!

    1. Hello Rainer,

      Thanks for your comment. I totally agree with your thoughts. Years ago I developed a storage server mostly used for DICOM medical images. It was mostly written in C using Visual Studio from Microsoft. All functions return 0 when all went well and a negative number when an issue was encountered. At some point we developed a command line interface (CLI) in C#. C# is a OO programming language which supports exceptions. The approach was to wrap the C API with a marshal and unmarshal calls so it could be invoked from C#. The C code returned values (did not throw exceptions) so that was the initial approach. As the C# code grew, then the code was added to check for negative returned values. When one was encountered the proper exception was thrown.
      I agree that it is best to always use exceptions when dealing with OO languages and returned values when dealing with non-OO ones.

      Regards;

      John

Leave a Reply

Your email address will not be published. Required fields are marked *

This site uses Akismet to reduce spam. Learn how your comment data is processed.