Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Move supported operating system check out of SystemInfo constructor #1680

Merged
merged 5 commits into from Jul 18, 2021

Conversation

KyongSik-Yoon
Copy link
Contributor

#1671 not worked for me.
Run CI with snapshot version but I saw same error message below.

[ERROR] display as string  Time elapsed: 0.016 s  <<< ERROR!
java.lang.UnsupportedOperationException: Operating system not supported: JNA Platform type 2
Caused by: java.lang.UnsupportedOperationException: Operating system not supported: JNA Platform type 2

JNA could recognize current os in static block when failed on a constructor of SystemInfo class.
So I tried check unknown environment by JNA method instead of OSHI. At the result my CI passed successfully.
I'm not sure what is exact reason. But I just think that JNA and OSHI depends on many static initialization and it's make not easy to assume proper working.

In my opinion, static blocks and variables should be minimized.
But if OSHI have important reason to check unsupported environment in constructor this MR need to approved.

There is remained riddle why OSHI's static variable and JNA static method result don't have consistency.
Anyway, This change working for me.
Please consider enough.

sam added 3 commits June 25, 2021 16:29
currentPlatform variable initialized in static block of this class.
But it's also accessed via constructor for validate unsupported environment.
This variable possible not initialized yet when access make a instance use constructor.
I'm already met this kind of issue when I run unit test use this library.
I think one more variable init checking required, at least If constructor want to check unsupported or not.
oshi#1671 not worked for me.
Run CI with snapshot version but I saw same error message below.
```
[ERROR] display as string  Time elapsed: 0.016 s  <<< ERROR!
java.lang.UnsupportedOperationException: Operating system not supported: JNA Platform type 2
Caused by: java.lang.UnsupportedOperationException: Operating system not supported: JNA Platform type 2
```
JNA could recognize current os in static block when failed on a constructor of SystemInfo class.
So I tried check unknown environment by JNA method instead of OSHI. At the result my CI passed successfully.
I'm not sure what is exact reason. But I just think that JNA and OSHI depends on many static initialization and it's make not easy to assume proper working.

In my opinion, static blocks and variables should be minimized.
But if OSHI have important reason to check unsupported environment in constructor this MR need to approved.

There is remained riddle why OSHI's static variable and JNA static method result don't have consistency.
Anyway, This change working for me.
Please consider enough.
@coveralls
Copy link

coveralls commented Jul 15, 2021

Coverage Status

Coverage decreased (-0.1%) to 85.85% when pulling f4ca71b on KyongSik-Yoon:master into 9ae88cc on oshi:master.

@dbwiddis
Copy link
Member

Thank you for testing and for the new proposed fix. I don't disagree with the code, but until I understand exactly what the root cause of the error is, I am hesitant to accept it. It could be some sort of race condition that your proposal appears to fix but doesn't.

I would really like to try to reproduce this myself. Can you tell me what CI system you are using? If this is not pure Java, what language is involved (is this Kotlin?).

Based on the initial report I suspect having the code in the constructor may be the root cause, and your suggested fix may not be permanent. There is probably a bug in some external program somewhere and it is important to try to reproduce it and fix it, rather than just trying workarounds.

@dbwiddis
Copy link
Member

In my opinion, static blocks and variables should be minimized.

Generally true, but:

  1. This is the top-level entry point into OSHI. It will always be called.
  2. The operating system being used will never change. There is no reason to recalculate which operating system is in use every time you re-instantiate a SystemInfo object.

The existing code follows the order of initialization of static variables/execution of static blocks in Java. If non-Java code is not observing this convention, there is a bug in that non-Java code that should be fixed, and fixing that code requires finding a reproducible example. If it is not a bug but a "feature" of that other code (e.g., Kotlin treats default constructors differently) then the reason that language violates Java conventions should be completely understood so it can be properly defended against.

@KyongSik-Yoon
Copy link
Contributor Author

KyongSik-Yoon commented Jul 16, 2021

Yes, That's important to find root cause.
I'm using GitLab CI on premise.
My application has both java and kotlin code.
CI runs maven unit tests that implemented using kotlin and kotest framework.
This error also happened when run maven install or test task without CI.
Maven install and test task run entire unit test of our product.
An exception only occurred in this phase but it's not happened if run only single unit test.

@KyongSik-Yoon
Copy link
Contributor Author

KyongSik-Yoon commented Jul 16, 2021

I was test one more thing.
An exception also happened with written by java code has same logic.
It just called constructor of SystemInfo class.

  • Code
public class StorageOnSystemJava {
    public StorageOnSystemJava(SystemInfo info) {
    }
}
  • Test
class StorageOnSystemJavaTest {
    @Test
    void run() {
        new StorageOnSystemJava(new SystemInfo());
    }
}
  • Error
[ERROR] Tests run: 1, Failures: 0, Errors: 1, Skipped: 0, Time elapsed: 0 s <<< FAILURE! - in com.aries.data.StorageOnSystemJavaTest
[ERROR] run  Time elapsed: 0 s  <<< ERROR!
java.lang.UnsupportedOperationException: Operating system not supported: JNA Platform type 2
	at com.aries.data.StorageOnSystemJavaTest.run(StorageOnSystemJavaTest.java:9)

As I know, kotlin is Interoperable with java perfectly.
It's not possible of Kotlin have other strategy for constructor.
Kotlin just compiled to byte code.

@KyongSik-Yoon
Copy link
Contributor Author

KyongSik-Yoon commented Jul 16, 2021

In my opinion, static blocks and variables should be minimized.

Generally true, but:

  1. This is the top-level entry point into OSHI. It will always be called.
  2. The operating system being used will never change. There is no reason to recalculate which operating system is in use every time you re-instantiate a SystemInfo object.

The existing code follows the order of initialization of static variables/execution of static blocks in Java. If non-Java code is not observing this convention, there is a bug in that non-Java code that should be fixed, and fixing that code requires finding a reproducible example. If it is not a bug but a "feature" of that other code (e.g., Kotlin treats default constructors differently) then the reason that language violates Java conventions should be completely understood so it can be properly defended against.

I agree with efficiency is very important.
But when I see SystemInfo class I couldn't understand why this class provide two way for access of fields or method.
I think this problem will not happened if SystemInfo class doesn't provide access way by instance for getHardware method.
It's already existed as static variable in this class.

@dbwiddis
Copy link
Member

Thank you for the detailed description of the problem and the simplified test case. I will try to reproduce it myself using that code.

@dbwiddis
Copy link
Member

Hi @KyongSik-Yoon I'm still trying to get to the root cause here.

I have some questions about your testing setup.

You indicated "This error also happened when run maven install or test task without CI." and the debug output above shows only a single test running. But in your earlier report you said "When I run single unit test. There is no problem." Can you clarify what you mean by these two statements?

  1. Are you running a multi-threaded test setup (one JVM, multiple threads, only one initialization of static final variables)?
  2. Are you running a test setup with multiple forks/JVM instances (static final variables initialized once in each fork)?

Are you able to edit OSHI's code to add some log debug statements or use a debugger on the process and execute with that modified code? The part where it looks like it's failing is where it checks if (Platform.isWindows()) inside a static method. It would be useful to print debug output for the result of that check by itself, and also the value of Platform.getOsType().

@dbwiddis dbwiddis changed the title Initialize static variable from static method instead of static block (with another approach) Move supported operating system check out of constructor Jul 18, 2021
@dbwiddis dbwiddis changed the title Move supported operating system check out of constructor Move supported operating system check out of SystemInfo constructor Jul 18, 2021
@dbwiddis dbwiddis merged commit 1d96b5e into oshi:master Jul 18, 2021
dbwiddis added a commit that referenced this pull request Jul 18, 2021
…1680)

* Initialization timing issue for static variable

currentPlatform variable initialized in static block of this class.
But it's also accessed via constructor for validate unsupported environment.
This variable possible not initialized yet when access make a instance use constructor.
I'm already met this kind of issue when I run unit test use this library.
I think one more variable init checking required, at least If constructor want to check unsupported or not.

* Initialize static variable from static method instead of static block

* Initialize static variable from static method instead of static block

#1671 not worked for me.
Run CI with snapshot version but I saw same error message below.
```
[ERROR] display as string  Time elapsed: 0.016 s  <<< ERROR!
java.lang.UnsupportedOperationException: Operating system not supported: JNA Platform type 2
Caused by: java.lang.UnsupportedOperationException: Operating system not supported: JNA Platform type 2
```
JNA could recognize current os in static block when failed on a constructor of SystemInfo class.
So I tried check unknown environment by JNA method instead of OSHI. At the result my CI passed successfully.
I'm not sure what is exact reason. But I just think that JNA and OSHI depends on many static initialization and it's make not easy to assume proper working.

In my opinion, static blocks and variables should be minimized.
But if OSHI have important reason to check unsupported environment in constructor this MR need to approved.

There is remained riddle why OSHI's static variable and JNA static method result don't have consistency.
Anyway, This change working for me.
Please consider enough.

* Update SystemInfo.java

* Add comment to explain why constructor is empty

Co-authored-by: sam <sam@jennifersoft.com>
Co-authored-by: Daniel Widdis <widdis@gmail.com>
@dbwiddis
Copy link
Member

So I've come to to the conclusion that it's probably Kotlin's "constructor first" behavior causing something strange here. I reverted to the OSHI 5.2.x setup for the 5.8.0 release.

I'd still like to try to definitively identify the problem, which may be a bug in how Kotlin interacts with the JVM/JLS.

@KyongSik-Yoon
Copy link
Contributor Author

KyongSik-Yoon commented Jul 19, 2021

Sorry for my late response.
It's working for me but I'm also wonder what's root cause.
I'll add some debug line to code and trace.

@KyongSik-Yoon
Copy link
Contributor Author

Hi @KyongSik-Yoon I'm still trying to get to the root cause here.

I have some questions about your testing setup.

You indicated "This error also happened when run maven install or test task without CI." and the debug output above shows only a single test running. But in your earlier report you said "When I run single unit test. There is no problem." Can you clarify what you mean by these two statements?

  1. Are you running a multi-threaded test setup (one JVM, multiple threads, only one initialization of static final variables)?
  2. Are you running a test setup with multiple forks/JVM instances (static final variables initialized once in each fork)?

Are you able to edit OSHI's code to add some log debug statements or use a debugger on the process and execute with that modified code? The part where it looks like it's failing is where it checks if (Platform.isWindows()) inside a static method. It would be useful to print debug output for the result of that check by itself, and also the value of Platform.getOsType().

  1. Are you running a multi-threaded test setup (one JVM, multiple threads, only one initialization of static final variables)?
    ==> I was run maven on IDE(IntelliJ). It's default configuration is single thread base. It's just possible with -T option.

  2. Are you running a test setup with multiple forks/JVM instances (static final variables initialized once in each fork)?
    ==>
    image
    As you can see maven default fork count is one. I'm using maven without any additional configuration.

@dbwiddis
Copy link
Member

But the error occurs when running under Kotlin testing (kotest) but not Java testing (junit)?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

None yet

3 participants